Информация об изменениях

Сообщение Re[44]: Что такое Dependency Rejection от 14.01.2024 18:56

Изменено 14.01.2024 19:06 ·

Re[44]: Что такое Dependency Rejection
Здравствуйте, Pauel, Вы писали:

P>·>Я не понимаю что такое заточка мока. Код с моками это просто код. Там где код общий, там он переиспользуется из разных мест. Там где не общий, там не переиспользуется. Мок это просто механизм передачи данных. Ровно такой же вопрос я могу спросить про твои параметры.

P>Это я имею ввиду начинку — то, что вы заложите в мок. С параметрами у нас ровно самый минимум — только таблица истинности, и сам код. У вас к этому добавляются еще и моки.
И пусть добавляются. Это просто способ передачи значений.

P>>>Проверка склейки — такого не будет. А вот поддерживает ли приложение такую функцию — нужно проверять интеграционным тестым.

P>·>Не всегда нужно, в этом и суть. Интеграционный тест проверяет интеграцию, что компоненты работают вместе. А не каждую функцию приложения.
P>То-то и оно. Вот вы снова сами себе и ответили
Причём тут тогда "такую функцию"? Интеграция это не "такая функция".

P>>>И ваши моки не избавляют этого.

P>·>Моки не избавляют. А помогают избавляться.
P>Нисколько. Моки прибивают вас к конкретному дизайну — см пример про инлайн кода.
Вас может прибивают, нас — не прибивают.

P>Этот пример демонстрирует особенность — даже ИДЕ перестает помогать.

Помогать в чём? Ещё раз — инлайнинг и т.п. — это средство, а не цель. С какой целью что инлайнить-то?

P>А вот если вы отказываетесь от моков, соотвественно, ИДЕ вам в помощник — вдруг рефакторинг inline начинает работать сам собой

Ок, расскажи от каких моков надо избавиться, чтобы рефакторинг inline для str.length() заработал?

P>>>Вариантов дизайна всегда больше одного

P>·>Именно. Это всё субъективщина. Эти варианты у тебя меняются каждые год два, как женские шляпки. А вот требование ресурсов, использование сети-тредов-диска — это объективные критерии. На них я и ориентируюсь, а не на последние веяния моды.
P>Дизайн вообще должен меняться постоянно, непрерывно. А вы выбираете такой подход, что изменения раз в год-два. О том я и говорю — вы пилите монолит.
Зачем?! Это ты из мира js говоришь, где каждую неделю новый фреймворк выходит и надо всё переписать? А каждые пол года очередной самый правильный дизайн появляется от фаулеров и надо срочно всю архитектуру перекраивать?!

P>>>Есть же пример Буравчика. Вам мало, надо к абсурду свести?

P>·>В смысле ты хочешь сказать, что его пример на хаскель не переписывается? Переписыватеся, с монадами и т.п. Но проще он не станет. Поэтому вопрос и остаётся — накой. Твои предложения "улучшить" я не понял в чём же улучшение.
P>Я ж объяснил — таблица истинности рядом с тестом и кодом, моки-стабы тоньше некуда. Вы рассматриваете систему в отрыве от тестов. Это категрически неверно. Даже конкретный деплоймент не имеет смысла без тестов — чемодан без ручки. Нужно проверить, а все ли путём, без тестов это никак. Более того — поменялись внешние зависимости — снова нужны тесты.
Это мы уже обсуждали, в проде мы не тестируем, это дичь какая-то из прошлого века.

P>Соответсвенно нужно выбирать такое решение, которое проще мейнтейнить, с учетом тестов.

Угу. И моки тут помогают.

P>>>В стиле ТДД — написали тесты на моках, а потом и дизайн вышел соответствующим

P>·>Нет.
P>У вас похоже особое видение каждого термина, который мы обсуждаем. ТДД по Кенту Беку целиком про дизайн.
P>Вот код теста — как только вы его написали, решение о том, как будут протягиваться зависимости, уже принято
ТДД диктует необходимость того, чтобы код был тестируемый, а не про то куда как тянуть зависимости. Единственно что он может диктовать, что зависимостями надо управлять явно, чтобы можно было прокидывать тестовые.

P>>>·>Ну "правильного" дизайна ты предолжить просто не смог. Т.к. это более менее реальный проект с нетривиальной бизнес-логикой. А твои штуки вменяемо работают только на хоумпейджах.

P>>>Какой еще "правильный"? Я продемонстрировал, что минимальными усилиями можно свести тесты к таблице истинности и хранить все это максимально близко к коду.
P>·>Я видимо не понял. Где какие таблицы?
P> 1,2,3,4,5,6 // и так на каждую строчку в табличке
Не понимаю. Ты, похоже, стиль кода путаешь с дизайном. А дизайн с управлением зависимостями. Ну сделай себе таблицу истинности и с моками, и с параметрами:
void truthTable(int x, int y, int z) {
   when(dep.getX()).thenReturn(x);
   assert testSubject.doSomething(y) == z;
}

@Test withTableAsYouWish() {
   truthTable(1, 2, 5);
   truthTable(10, 3, 42);
}

Можешь и аннотации забабахать как у тебя выше. Или цикл. Или на разные тесты (чтобы имена давать осмысленные).
@params(
    1,2,3,4,5,6  // и так на каждую строчку в табличке
)
@params(
    8,4,2,1,3,5 // точно так же и исключения прокидывают
)
@Test withTableAsYouWish(x, y, z) {
   when(dep.getX()).thenReturn(x);
   assert testSubject.doSomething(y) == z;
}

Аннотации мне не нравятся, т.к., например, отладчиком сложно пройтись.

P>>>Связывание и есть wiring код. Основная обязанность контроллера — именно это. И тестировать связку, как следствие.

P>·>Нет, обязанность контрллера связывать входящие запросы с бизнес-логикой. wiring code — это отдельный код для связывания компонент друг с другом.u
P>Вы сейчас в слова играете. Сами же пишете — "обязанность контроллера связывать"
Ну не просто "связывать", а _что_ связывать. Шнурки связывать, контроллер, например, не умеет, хоть с моками, хоть без.
Связывание компонент это не то же, что связывание запросов с логикой.

P> Только еще вводите понятие wiring code.

Ок. Возможно я использовал неизвестную тебе терминологию. Это из мира управления зависимостей. "wiring up dependencies", "autowiring", под я под wiring code здесь имел в виду "composition root".

P>Часть кода связывания будет вне контролера, и правила вы водите сами, это и есть тот дизайн. Прокидываете через dependency injection — значит внутри контролера этого не будет.

Да похрен на дизайн. Это лишь меняет место где "волшебство" происходить будет.

P>Прокидываете now в контролере — эту часть логично исключить из dependency injection.

В контроллере now откуда возьмётся?

P>Смысл помещения кода связывания в контроллер в том, что бы легче было интеграцию тестировать, делая самое важно явным.

Не понял, чем легче?

P>>>·>Что за ерунда? Откуда там null возьмётся? Что за "повесите на dependency injection"? источник времени — это зависимость CalendarLogic, инжектится через конструктор. ЧПФ, забыл опять?

P>>>Сами спросили — сами ответили.
P>·>Откуда _у тебя_ null берётся? У меня никаких null тут нет.
P>У вас CalendarLogic принимает зависимость к конструкторе. Технически, эта зависимость может быть и null, и предзаписаной, и привязаной к контексту — не принципиально.
Это тут причём? Технически передавая через параметр твой now — тоже может быть null и т.п. Всё то же самое.
Но есть очень важная разница в том, что конструктор таких зависимостей (по крайней мере подавляющего большинства зависимостей) вызывается в момент старта приложения, в composition root. И банальная requireNotNull (если яп не поддерживает not-null типы на этапе компиляции) в худшем случае будет железно ронять деплоймент, если конфиги плохие.
В твоём же случае впихивание null будет происходить только при вызове функции приложения. Поэтому вам и требуются тесты всего уже после "успешгого" деплоя в проде.

P>Ваша задача — сделать так, что бы в контролер этот CalendarLogic пришел с нужной завимостью, а не произвольной. Соответсвенно, гарантии фиксируем через интеграционный тест — конкретный use case собран как положено. И вот здесь внутренности реализации вообще роли не играют — проверяется интеграция, а не внутреннее устройство.

А как ты будешь такой и-тест писать, если у тебя будет Time.Now? Что ассертить-то будешь? Как отличать два разных вызова Time.Now — клиентский от серверного? Впрочем, ты уже ответил "никак". Или я что-то пропустил?

P>>>О — InstantSource появился. Значит инжектиться может не один единственный вариант с Now, а минимум два.

P>·>А ты вообще код мой читал? В первой строчке код-сниппета
Автор: ·
Дата: 28.12.23
. Ты по-моему сам со своими фантазиями споришь, я тебе только мешаю.

P>Вы похоже сами свой код не читали, т.к. у вас недоумение, откуда может быть null. И ежу понятно — будет передан в конструктор или явно, или неявно. Правильно это или нет — скажут тесты.
Так же можно явно или неявно передать now=null в твой nextFriday(now). Ты опять вместо того, чтобы сравнивать два подхода начинаешь общие слова говорить.

P>>>Где у вас тестирование инжекции? А ну как на проде все инжектиться будет по старому?

P>·>В прод-classpath не видны моки и вообще вся тестовая белиберда. Код не скомпилится просто, если туда правильную зависимость не вписать.
P>Успокойтесь — у вас есть провайдер time, вам нужно больше одной реализации — время может тащиться из разных источников. И ваш CalendarLogic работает с любой из таких зависимостей.
P>А щас вы расказываете басню, что компилер как то отличит один провайдер времени от другого времени с таким же интерфейсом
Компилятор может отличать имена символов. Это же в коде видно и на ревью всё очень очевидно — где какое поле куда передаётся. Впрочем, если у тебя всё равно паранойя по этому поводу, лечится просто. Заводим что-то вроде
class TimeProviders
{
   private final EventTimestampSource etSource;
   
   InstantSource systemClock() { return Clock.systemUTC(); }
   InstantSource eventTimestampClock() { return etSource;  }
}

далее в тестах:
when(timeProviders.systemClock()).thenReturn(TestData.SystemClock);
when(timeProviders.eventTimestampClock()).thenReturn(TestData.ETClock);
...
assert someResult1.dateAAA == TestData.SystemClock.now();
...
assert someResult2.dateXYZ == TestData.ETClock.now();


Это, мягко говоря, немного другая ситуация, чем с modify(oldUser, newUser) — т.к. тут по строчке кода никак не видно что куда передаётся и не перепутаны ли параметры.

P>>>А как вы понимаете, что такое отложеный вызов? Недавно вам непонятно было что такое dependency injection, потом выяснилось, что все вам известно.

P>·>Мне не было понятно, что ты под этим понимаешь.
P>Прямой вызов — ваша функция вызовется прямо сейчас. Отложеный — потом. Например, вы сначала делаете запись, что за функция вызовется, с какими параметрами и тд, а в нужный момент сервис делает собственно сам вызов.
Какое это имеет отношение к разговору?

P>·>Устанавливаем в тестах, что "у нас на сервере now это 21:00:01, а на клиенте "21:00:02". И в тестах можно ассертить что в нужных случаях ожидаемое время. Что ты будешь делать со своим синлтоном Time.Now — ты так и не рассказал. Вообще я не понимаю как ты такое можешь использовать и тем более тестировать, просвети.

P>Просто разные аргументы.
Аргументы чего? Откуда ты знаешь, что значения этих аргументов именно Time.Now?

P>·>Что синглтоны — плохо учат ещё в детском садике, ясельной группе.

P>Вы наверное не поняли — никаких синглтонов, только параметры.
Откуда возьмутся значения этих параметров в месте вызова?

P>>>А еще можно обойтись без моков И свести к таблице истинности, поскольку интеграционный тест всё равно понадобится.

P>·>Ты заявил, что никак не сможешь время протестировать.
P>Мне не надо время тестировать. Мне нужно тестировать логику, в которой один или несколько параметров — время. Вы это время протаскиваете через конструктор косвенно, в виде провайдера. Я протаскиваю через параметр функции, явно.
Надо где-то тестировать, что ты протаскиваешь именно Time.Now а не что-то другое.

P>>>Ну и что?

P>·>Что каждую копию надо покрывать тестом. Ты правда сейчас собираешься копи-пасту продвигать как правильный дизайн?
P>Интеграционным, которых ровно столько же, сколько и у вас.
Нет. У меня пирамида тестов. А у тебя — либо цилиндр, либо дырявое решето.

P>>>Затем, что признак хорошего кода это его гибкость, которая проявляется в т.ч. в том, что код можно трансформировать, инлайнить, итд, да хоть вовсе от него избавиться.

P>·>Если при этом нужно плодить копипасту, то это явно что-то не то. И неясно причём тут инлайн и гибкость кода. str.length() — это негибкий код?!
P>Вы сейчас перевираете.
Неперивираю, ты просто свои слова забыл. Напоминаю: " гибкость, которая проявляется в т.ч. в том, что код можно трансформировать, инлайнить". А дальше чистая логика:
"гибкий код → можно инлайнить" => "инлайнить нельзя → не гибкий код" см. https://en.wikipedia.org/wiki/Contraposition

P>Речь про инлайн вашего nextFriday(). Об ваш супер-крутой подход почему то ИДЕ спотыкается и вы проблемы в этом не видите.

Я спрашиваю "зачем инлайн"? Какую задачу-то решаем этим инлайном?!

P>>>Вы так и не рассказали, как будете решать ту самую задачу с фильтрами.

P>·>Рассказал и даже код показал,
Автор: ·
Дата: 28.12.23
насколько понял твою задачу. Если ещё остались вопросы, спрашивай.

P>Ваши тесты проверяют только тривиальные кейсы, на которых все зелено. В проде как правило кучка всего подряд.
Ну добавь эту твою кучку в тесты. Вопрос-то в чём?

P>>>И не надо.

P>·>Надо, если хотим тестировать поведение, а не детали имплементации.
P>Тестируйте базу, я ж не мешаю. Можете еще и операционку и джава рантайм сюда добавить
Я не базу тестирую, а запросы, которые строит мой код. А ты хз чего тестируешь.

P>>>Билдера запросов к базе данных. Вы что думаете, все sql запросы руками пишутся?

P>·>Да похрен как они пишутся, хоть левой пяткой. Это всё детали реализации. Главное что они работают ожидаемым способом.
P>Работают ожидаемым способом — это вы на примитивных фильтрах, что вы продемонстрировали, не проверите.
Ты так заявляешь, что продемонстированном этим кодом подходе нельзя написать фильтр какой тебе угодно непримитивный. С чего ты взял? Пиши фильтры какие придумаешь.

P>О том, что фильтр может вернуть много записей, вам может сказать или сам запрос, или его результат.

Как запрос может что-то сказать?

P>Второй вариант у вас отпадает, т.к. в тестах у вас не будет хрензнаетсколькилион записей в тестах

Зачем мне столько записей? В простом случае понадобится две записи — та которая я ожидаю, что она должна вернуться и та которая я ожидаю, что не должна вернуться.

P>>> Кое что, для аналитики — возможно. Но это большей частью всё равно перегоняется в орм или билдер. Есть орм — можно проверять построение логики запросов орм.

P>·>В смысле ты в своих тестах linq/etc тестируешь? Зачем? У linq свои тесты есть.
P>Это вы собираетесь базу тестировать
Опять врёшь. И цитаты, ясен пень, не будет.

P> и топите за поведение. А мне надо всего лишь построение запроса к орм проверить. Если орм не позволяет такого — надо смотреть его sql выхлоп, если и такого нету, значит придется делать так, как вы предлагаете.

"Надо" кому? Мы вроде говорим о тестировании ожиданий бизнеса. Ты правда считаешь, что пользователи ожидают определённый выхлоп sql?!

P>>>Запрос к базе должен соответсвовать бизнес-требованиям. Только проверяем мы это в т.ч. посредством метапрограммирования.

P>·>Что за бред? У тебя в FRD прописаны тексты запросов??! Прям где-то у вас есть бизнес-требование "select * from users where id=?" и "in: [fn1]"?! Приходит к вам клиент и говорит "а мне очень нужно чтобы out: [fn2], когда можете сделать?"
P>Что вас смущает? Запрос к бд должен возвращать именно те данные, что прописаны в бл или непосредственно следуют из бл, нефункциональных требований итд. Вас почему то тянет всё к абсурду сводить.
P>Кроме бизнес требований, у нас куча важных вещей — те самые нефункциональные. Их на порядок больше чем бизнес-требований.
P>И всё это нужно покрывать тестами, иначе любой дядел поломает цивилизацию
Ещё раз. У тебя в коде теста, в ожиданиях находится "in: [fn1]" и т.п. Как это соответствует "возвращать именно те данные, что прописаны в бл"? Где в бл вообще будет фигурировать in, fn1 и прочий select?

P>>>·>Цитату в студию или признавайся, что опять соврал.

P>·>Цитаты нет. Т.е. соврал.
P>Смотрите выше — вы фильтры предлагаете тестировать однострочными вырожденными кейсами. Как вы будете ловить те самые комбинации, не ясно.
Ну я не знаю твой код. Я разрешаю тебе писать кейсы двухстрочные и даже трёхстрочные!

P>>>Вы до сих пор не привели решение кроме "ищем в бд"

P>·>Я код
Автор: ·
Дата: 28.12.23
привёл. Что не устраивает-то??! "ищем" — это "find", английский язык знаешь?

P>Смотрите сами: assertThat(testSubject.find(new Filter.byEqual("Vasya"))).isPresent();
P>У вас testSubject это или репозиторий, который вы предлагаете с подключеной бд тестировать
Да.

P>Ни то, ни другое проблему с фильтрами не найдут.

Почему?

P>>>·>Куда смотреть? Поломал что?

P>>>Вам надо мок подфиксить, что бы время было той системы
P>·>Чё?
P>Смотрите — первая версия — вызываем бл прямо из контролера.
P>Ваши тесты — вы обмокали провайдер времени, передаете туда-сюда, включая тесты контролера
P>т.е. тесты контролера зависят от мока
Т.е. в тестах контроллера мы прописали ожидание, что время используется серверное, в соответствии с бизнес-требованиям.

P>Вторая — вызываем бл при чтении из очереди, при этом время должно быть клиенское.

Т.е. это значит, что _изменились_ бизнес-требования и теперь мы должны брать не серверное время, а клиентское. Ясен пень, это фукнциональное изменение и соответствующие тесты надо менять. Если у тебя ни один тест не упадёт, у меня для тебя плохие новости.

P>А контролер занят всего лишь тем, что кладет запрос в очередь.

P>А это значит, что ваши тесты контролера идут в топку, как и моки которые вы под это дело прикрутили.
В контроллере надо будет заменить использование systemClock() на eventTimestampClock() и поправить тесты, чтобы зафиксировать функциональное изменение.

P>>>Наша задача гарантировать что запросы соответствуют бизнес-требованиям. Что бы это гарантировать, я выбрал реализацию с метапрограммированием. Вы, очевидно, выбрали другое — гонять горстку данных к базе и обратно,

P>·>Покажи пример бизнес-требования.
P>зачем?
Чтобы понять какое отношение имеет метапрограммирование к тестированию ожиданий бизнес-требований.

P>>>и у вас кейс с вырожденным фильром до сих пор не покрыт тестом.

P>·>Я не видел этого кейса, и не могу нафантазировать какие у тебя возникли проблемы его покрыть.
P>А он и не виден, т.к. проблема тупо в количестве вариантов которые могут порождать фильтры. Та самая data complexity.
Ну а в телепанию я не умею.

P>>>Не знаю, что у вас с этим за проблема, тут рукописные логи годами ходят в одном и том же виде, тригерят эвенты, и ничего. А гарантировать сохранность стиля генерируемого кода почему то вам кажется рокетсаенсом.

P>·>Да причём тут стиль? Это мелкий чих. Я могу переписать например "SELECT ... X OR Y" на "UNION SELECT ... X ... SELECT ... Y" для оптитмизации. У тебя все тесты посыпятся и любая ошибка — увидишь после деплоя и прогона многочасовых тестов, в лучшем случае (а скорее всего только в проде). А у меня только те тесты грохнутся, если я реально ошибусь и что-то поломаю в поведении, ещё до коммита.
P>Очевидно, посыплются не все. Ну нет такой ситуации что бы билдер на каждый запрос лепил UNION. Отвалится скорее всего несколько кейсов, ради которых это все и затевается.
Ну этот кусочек может быть в большой группе запросов, выбирающих разные данные по-разному, но часть логики — общая.

P>>>Потому и стоит менять дизайн что бы это контролировать, а через di протаскивать только технологические зависимости.

P>·>Зато этот код очень простой, линейный и на 99% покрывается компилятором.
P>Не проверяется. Ваш CalendarLogic может принимать разные экземпляры — с текущим временем, клиентским, серверным, из параметра, итд. Всё что вам компилятор скажет, что типы нужные.
P>Ну и что?
P>Кто даст гарантию, что в проде вы правильно прописали зависимости для CalendarLogic? Интерфейс то один и то же
Гарантию, конечно, никто не даст, но покрыть тестами с моками тоже можно. Для этого моки и придумали.

>> Остальной 1% покрывается стартом приложения.

P>Еще меньшая гарантия.
Опять ты о гарантиях заговорил, опять опозоришься.

P>>>Очень просто — у нас уже есть тесты, одни должны сохраниться без каких либо изменений.

P>·>Я спросил как сделать так, чтобы такие тесты были, ты ответил
Автор: Pauel
Дата: 31.12.23
"Никак.". Т.е. тестов у вас таких просто нет.

P>Вы вопрос свой прочитать в состоянии? как именно ты собираешься в "curl" и-тестах ассертить
P>Буквально curl тест все что может, это сравнить выхлоп, и всё. Если там время не фигурирует, то никак.
Ну, допустим, фигурирует. И чем это тебе поможет?

P>Если же вы имеете ввиду интеграционные, то будет все ровно как и у вас.

С моками? Ну дык об чём спор-то тогда? Ведь именно моком я могу протащить тестовые значения now в нужные места. Там, где ты предлагаешь Time.Now синглтон, в который ты ничего протащить не сможешь.

P>·>Я о чём и говорю. В каких-то случаях у тебя могут меняться запросы. Ты уже сам говорил о случае с пустым значением, которое ты заявил, что и-тестами тестировать не собираешься.

P>·>И это могут быть нефункиональные требования, а какие-то технические детали, оптимизации. Простой пример. Скажем, если идёт выборка по небольшому списку параметров, то строится запрос с "IN (?,?)", иначе создаётся временная табличка и с ней джойнится.
P>Бинго! Оптимизация, нефункциональные требования, технические особенности — все это имеет чудовищное влияние, намного больше, чем сама бизнес-логика.
P>Если вы потеряете оптимизацию — приплызд. и тест нужен зафиксировать эту особенность что бы сохранить при изменениях.
Это уже будет перф-тест и к обсуждаемой теме не относится. Я пока обсуждаю именно и/ю-тесты. И суть в том, что твоё "интеграционные тесты строятся по функциям, а не по запросам к базе" — несостоятельный подход. Т.е. в бизнес-требованиях будет одна функция "выбрать по списку", следовательно и-тест у тебя тоже один, а то что от размера списка может срабатывать разный код, генеряться разные запросы и т.п. — это ты просто не протестируешь и будешь ловить проблемы на проде.

P>>>А вот запросы к базе вещь слишком сложная и ответственная, потому стоит использовать разные инструменты, а не рассказывать, что вам запретили метапрограммирование

P>·>Именно. Поэтому надо их выполнять и проверять результаты, неважно мета там или фета. Никто вам ничего не запрещает, а мой поинт в том, что важно что запросы делают, а не какие инструменты используются.
P>См выше — все нефунциональные вещи нужно проверять и фиксировать тестами.
Это не оправдывает твой подход, что функциональные тесты должны тестировать метапрограммирование.

P>>>Видите — не так, как вы привыкли и сразу ужос-ужос-мрак-и-смэрть

P>·>Ну вроде это уже вс проходили и забили. Заталкивать тесты в прод-код... зачем??!
P>Затем, что это быстрый, дешовый и надежный способ узнавать о проблемах с приложением в целом
P>Покажите, как вы все эти кейсы обнаруживать будете.
Возвращаемся к старому спору. Я уже отвечал на это неоднократно: системы мониторинга, health probes и прочие операционные инструменты. Это не тестирование.

P>Полагаю ответ будет "у нас багов на проде нету" ?

Вот таких багов "протух-обновился сертификат" — и не должно быть. Такие вещи именно мониторингом отслеживаются заранее, чтобы пофиксить можно было до возникновения этой ситуации. А у вас серты протухают ВНЕЗАПНО, и обнаруживаются только тестами постфактум, что у вас в проде что-то где-то отвалилось... ну могу только пособолезновать вашим клиентам.

P>>>·>А конкретнее? С примером кода.

P>·>Именно. Примера кода я так и не дождусь.
P>насколько я понимаю, это я все еще жду вашего примера про фильтры, который решает ту самую data complexity
Какую ту самую? Я никакой "data complexity" не видел и телепатией не обладаю, чтобы выдать какое-то решение для задачи о которой я не слышал.

P>>>Это и есть тесты билдера. Только конкретного, в котором заложено много различных вырожденных кейсов. linq не в курсе, что вы вытаскиваете всю базу, ему по барабану. Мне — нет.

P>·>Linq выдаёт список результатов — вот и проверяй, что в этом списке — вся база или не вся.
P> Вход на фильтры полным перебором подавать или магией воздействовать?
Да так же, как у тебя. Только ассертить не буквальный текст запроса, а результат его выполнения.

P>>>Ну так найдите эту комбинацию. А их, мягко говоря, немало. Как будете искать?

P>·>Анализом требовний, кода, данных, етс. Обычный corner case analysis. А как вы будете искать?
P>Вот-вот. Анализом. Только результаты нужно зафиксировать тестами таким образом, что бы следующиее изменение не сломало всё подряд втихую.
Да, верно.

P>>>И вы так прямо и втащите coq в любом проект на джаве?

P>·>Нет. А вы таки втащили доказательное программированипе в js?
P>Зачем? Доказывать свойства можно и без coq, только результаты нужно выражать в дизайне и подкреплять тестами.


P>>>Еще раз — фильтр не хардкодится, а строится.

P>·>Я потерял суть. О чём речь вообще? Пример в студи. КОД!
P>Вы когда нибудь видели дашборды где фильтры можно выбирать хрензнаетсколькилионом кнопочек и полей ввода?
Насколько мне доводилось видеть, они обычно через & соединяются. Т.е. список независимых фильтров. Сложность возникает когда фильтры между собой взаимосвязаны, но обычно такое избегают, т.к. юзерам такая compexity тоже не под силу, и использовать нормально всё равно не получается.

P>>>Нет, не называется. Разница между code-first и design-first не формате и расширении файла, как вам кажется, а в очередности деливери.

P>·>Верно. В design-first деливерится манифест first.
P>Не просто манифест — а любое описание, абы ваши пиры могли его понять и цикл разработки был быстрым.
Что значит за любое описание? Манифест это и есть описание api.

P>>>В том то и дело — ваша задача накидать интерфейсы-модели так, что бы имплементации было 0. Тогда можно деливерить сразу, а вот будет ли деливериться имплементация, и когда — уже дело десятое.

P>·>Ну да. Откуда же берутся конфиги/тенанты/http-запросы?
P>Появятся после того, как вы заделивирите АПИ, например спустя год, когда у вас уже версия 1.x. И вот здесь вы решите перейти на rust, то в этот момент крайне странно топить за design-first — у нас уже готовое приложение версии 1.x, и нужно сделать так, что бы манифест отрендерился — с учетом тенантов и прочих вещей, включая not implemented. Или вы собираетесь бегать по отделам и рассказывать "а чо вы это делаете — мы же на джаве год назад это решили не делать" ?
У вас уже есть имплементация и мы вот наконец спустя год выкатываем манифест api, но ты всё ещё это называешь design-first?! Нет, это типичный code-first — когда прыгать надо, думать некогда.

P>>>Вы вместо уточнения пытаетесь в телепатию играть. Если у нас готовое приложение, то конечно же генерируется — и странно было бы иначе делать.

P>·>Угу. ЧТД. "готовое приложение" == "code-first".
P>Я вам говорю откуда возьмется апи в готовом, если вы решите уже готовое переписать на другом языке.
Причём тут design-first?
Re[44]: Что такое Dependency Rejection
Здравствуйте, Pauel, Вы писали:

P>·>Я не понимаю что такое заточка мока. Код с моками это просто код. Там где код общий, там он переиспользуется из разных мест. Там где не общий, там не переиспользуется. Мок это просто механизм передачи данных. Ровно такой же вопрос я могу спросить про твои параметры.

P>Это я имею ввиду начинку — то, что вы заложите в мок. С параметрами у нас ровно самый минимум — только таблица истинности, и сам код. У вас к этому добавляются еще и моки.
И пусть добавляются. Это просто способ передачи значений.

P>>>Проверка склейки — такого не будет. А вот поддерживает ли приложение такую функцию — нужно проверять интеграционным тестым.

P>·>Не всегда нужно, в этом и суть. Интеграционный тест проверяет интеграцию, что компоненты работают вместе. А не каждую функцию приложения.
P>То-то и оно. Вот вы снова сами себе и ответили
Причём тут тогда "такую функцию"? Интеграция это не "такая функция".

P>>>И ваши моки не избавляют этого.

P>·>Моки не избавляют. А помогают избавляться.
P>Нисколько. Моки прибивают вас к конкретному дизайну — см пример про инлайн кода.
Вас может прибивают, нас — не прибивают.

P>Этот пример демонстрирует особенность — даже ИДЕ перестает помогать.

Помогать в чём? Ещё раз — инлайнинг и т.п. — это средство, а не цель. С какой целью что инлайнить-то?

P>А вот если вы отказываетесь от моков, соотвественно, ИДЕ вам в помощник — вдруг рефакторинг inline начинает работать сам собой

Ок, расскажи от каких моков надо избавиться, чтобы рефакторинг inline для str.length() заработал?

P>>>Вариантов дизайна всегда больше одного

P>·>Именно. Это всё субъективщина. Эти варианты у тебя меняются каждые год два, как женские шляпки. А вот требование ресурсов, использование сети-тредов-диска — это объективные критерии. На них я и ориентируюсь, а не на последние веяния моды.
P>Дизайн вообще должен меняться постоянно, непрерывно. А вы выбираете такой подход, что изменения раз в год-два. О том я и говорю — вы пилите монолит.
Зачем?! Это ты из мира js говоришь, где каждую неделю новый фреймворк выходит и надо всё переписать? А каждые пол года очередной самый правильный дизайн появляется от фаулеров и надо срочно всю архитектуру перекраивать?!

P>>>Есть же пример Буравчика. Вам мало, надо к абсурду свести?

P>·>В смысле ты хочешь сказать, что его пример на хаскель не переписывается? Переписыватеся, с монадами и т.п. Но проще он не станет. Поэтому вопрос и остаётся — накой. Твои предложения "улучшить" я не понял в чём же улучшение.
P>Я ж объяснил — таблица истинности рядом с тестом и кодом, моки-стабы тоньше некуда. Вы рассматриваете систему в отрыве от тестов. Это категрически неверно. Даже конкретный деплоймент не имеет смысла без тестов — чемодан без ручки. Нужно проверить, а все ли путём, без тестов это никак. Более того — поменялись внешние зависимости — снова нужны тесты.
Это мы уже обсуждали, в проде мы не тестируем, это дичь какая-то из прошлого века.

P>Соответсвенно нужно выбирать такое решение, которое проще мейнтейнить, с учетом тестов.

Угу. И моки тут помогают.

P>>>В стиле ТДД — написали тесты на моках, а потом и дизайн вышел соответствующим

P>·>Нет.
P>У вас похоже особое видение каждого термина, который мы обсуждаем. ТДД по Кенту Беку целиком про дизайн.
P>Вот код теста — как только вы его написали, решение о том, как будут протягиваться зависимости, уже принято
ТДД диктует необходимость того, чтобы код был тестируемый, а не про то куда как тянуть зависимости. Единственно что он может диктовать, что зависимостями надо управлять явно, чтобы можно было прокидывать тестовые.

P>>>·>Ну "правильного" дизайна ты предолжить просто не смог. Т.к. это более менее реальный проект с нетривиальной бизнес-логикой. А твои штуки вменяемо работают только на хоумпейджах.

P>>>Какой еще "правильный"? Я продемонстрировал, что минимальными усилиями можно свести тесты к таблице истинности и хранить все это максимально близко к коду.
P>·>Я видимо не понял. Где какие таблицы?
P> 1,2,3,4,5,6 // и так на каждую строчку в табличке
Не понимаю. Ты, похоже, стиль кода путаешь с дизайном. А дизайн с управлением зависимостями. Ну сделай себе таблицу истинности и с моками, и с параметрами:
void truthTable(int x, int y, int z) {
   when(dep.getX()).thenReturn(x);
   assert testSubject.doSomething(y) == z;
}

@Test withTableAsYouWish() {
   truthTable(1, 2, 5);
   truthTable(10, 3, 42);
}

Можешь и аннотации забабахать как у тебя выше. Или цикл. Или на разные тесты (чтобы имена давать осмысленные).
@params(
    1,2,3,4,5,6  // и так на каждую строчку в табличке
)
@params(
    8,4,2,1,3,5 // точно так же и исключения прокидывают
)
@Test withTableAsYouWish(x, y, z) {
   when(dep.getX()).thenReturn(x);
   assert testSubject.doSomething(y) == z;
}

Аннотации мне не нравятся, т.к., например, отладчиком сложно пройтись.

P>>>Связывание и есть wiring код. Основная обязанность контроллера — именно это. И тестировать связку, как следствие.

P>·>Нет, обязанность контрллера связывать входящие запросы с бизнес-логикой. wiring code — это отдельный код для связывания компонент друг с другом.u
P>Вы сейчас в слова играете. Сами же пишете — "обязанность контроллера связывать"
Ну не просто "связывать", а _что_ связывать. Шнурки связывать, контроллер, например, не умеет, хоть с моками, хоть без.
Связывание компонент это не то же, что связывание запросов с логикой.

P> Только еще вводите понятие wiring code.

Ок. Возможно я использовал неизвестную тебе терминологию. Это из мира управления зависимостей. "wiring up dependencies", "autowiring", под я под wiring code здесь имел в виду "composition root".

P>Часть кода связывания будет вне контролера, и правила вы водите сами, это и есть тот дизайн. Прокидываете через dependency injection — значит внутри контролера этого не будет.

Да похрен на дизайн. Это лишь меняет место где "волшебство" происходить будет.

P>Прокидываете now в контролере — эту часть логично исключить из dependency injection.

В контроллере now откуда возьмётся?

P>Смысл помещения кода связывания в контроллер в том, что бы легче было интеграцию тестировать, делая самое важно явным.

Не понял, чем легче?

P>>>·>Что за ерунда? Откуда там null возьмётся? Что за "повесите на dependency injection"? источник времени — это зависимость CalendarLogic, инжектится через конструктор. ЧПФ, забыл опять?

P>>>Сами спросили — сами ответили.
P>·>Откуда _у тебя_ null берётся? У меня никаких null тут нет.
P>У вас CalendarLogic принимает зависимость к конструкторе. Технически, эта зависимость может быть и null, и предзаписаной, и привязаной к контексту — не принципиально.
Это тут причём? Технически передавая через параметр твой now — тоже может быть null и т.п. Всё то же самое.
Но есть очень важная разница в том, что конструктор таких зависимостей (по крайней мере подавляющего большинства зависимостей) вызывается в момент старта приложения, в composition root. И банальная requireNotNull (если яп не поддерживает not-null типы на этапе компиляции) в худшем случае будет железно ронять деплоймент, если конфиги плохие.
В твоём же случае впихивание null будет происходить только при вызове функции приложения. Поэтому вам и требуются тесты всего уже после "успешгого" деплоя в проде.

P>Ваша задача — сделать так, что бы в контролер этот CalendarLogic пришел с нужной завимостью, а не произвольной. Соответсвенно, гарантии фиксируем через интеграционный тест — конкретный use case собран как положено. И вот здесь внутренности реализации вообще роли не играют — проверяется интеграция, а не внутреннее устройство.

А как ты будешь такой и-тест писать, если у тебя будет Time.Now? Что ассертить-то будешь? Как отличать два разных вызова Time.Now — клиентский от серверного? Впрочем, ты уже ответил "никак". Или я что-то пропустил?

P>>>О — InstantSource появился. Значит инжектиться может не один единственный вариант с Now, а минимум два.

P>·>А ты вообще код мой читал? В первой строчке код-сниппета
Автор: ·
Дата: 28.12.23
. Ты по-моему сам со своими фантазиями споришь, я тебе только мешаю.

P>Вы похоже сами свой код не читали, т.к. у вас недоумение, откуда может быть null. И ежу понятно — будет передан в конструктор или явно, или неявно. Правильно это или нет — скажут тесты.
Так же можно явно или неявно передать now=null в твой nextFriday(now). Ты опять вместо того, чтобы сравнивать два подхода начинаешь общие слова говорить.

P>>>Где у вас тестирование инжекции? А ну как на проде все инжектиться будет по старому?

P>·>В прод-classpath не видны моки и вообще вся тестовая белиберда. Код не скомпилится просто, если туда правильную зависимость не вписать.
P>Успокойтесь — у вас есть провайдер time, вам нужно больше одной реализации — время может тащиться из разных источников. И ваш CalendarLogic работает с любой из таких зависимостей.
P>А щас вы расказываете басню, что компилер как то отличит один провайдер времени от другого времени с таким же интерфейсом
Компилятор может отличать имена символов. Это же в коде видно и на ревью всё очень очевидно — где какое поле куда передаётся. Впрочем, если у тебя всё равно паранойя по этому поводу, лечится просто. Заводим что-то вроде
class TimeProviders
{
   private final EventTimestampSource etSource;
   
   InstantSource systemClock() { return Clock.systemUTC(); }
   InstantSource eventTimestampClock() { return etSource;  }
}

далее в тестах:
when(timeProviders.systemClock()).thenReturn(TestData.SystemClock);
when(timeProviders.eventTimestampClock()).thenReturn(TestData.ETClock);
...
assert someResult1.dateAAA == TestData.SystemClock.now();
...
assert someResult2.dateXYZ == TestData.ETClock.now();


Это, мягко говоря, немного другая ситуация, чем с modify(oldUser, newUser) — т.к. тут по строчке кода никак не видно что куда передаётся и не перепутаны ли параметры.

P>>>А как вы понимаете, что такое отложеный вызов? Недавно вам непонятно было что такое dependency injection, потом выяснилось, что все вам известно.

P>·>Мне не было понятно, что ты под этим понимаешь.
P>Прямой вызов — ваша функция вызовется прямо сейчас. Отложеный — потом. Например, вы сначала делаете запись, что за функция вызовется, с какими параметрами и тд, а в нужный момент сервис делает собственно сам вызов.
Какое это имеет отношение к разговору?

P>·>Устанавливаем в тестах, что "у нас на сервере now это 21:00:01, а на клиенте "21:00:02". И в тестах можно ассертить что в нужных случаях ожидаемое время. Что ты будешь делать со своим синлтоном Time.Now — ты так и не рассказал. Вообще я не понимаю как ты такое можешь использовать и тем более тестировать, просвети.

P>Просто разные аргументы.
Аргументы чего? Откуда ты знаешь, что значения этих аргументов именно Time.Now и как это проверишь в тесте?

P>·>Что синглтоны — плохо учат ещё в детском садике, ясельной группе.

P>Вы наверное не поняли — никаких синглтонов, только параметры.
Откуда возьмутся значения этих параметров во всех местах вызова?

P>>>А еще можно обойтись без моков И свести к таблице истинности, поскольку интеграционный тест всё равно понадобится.

P>·>Ты заявил, что никак не сможешь время протестировать.
P>Мне не надо время тестировать. Мне нужно тестировать логику, в которой один или несколько параметров — время. Вы это время протаскиваете через конструктор косвенно, в виде провайдера. Я протаскиваю через параметр функции, явно.
Надо где-то тестировать, что ты протаскиваешь именно Time.Now а не что-то другое.

P>>>Ну и что?

P>·>Что каждую копию надо покрывать тестом. Ты правда сейчас собираешься копи-пасту продвигать как правильный дизайн?
P>Интеграционным, которых ровно столько же, сколько и у вас.
Нет. У меня пирамида тестов. А у тебя — либо цилиндр, либо дырявое решето.

P>>>Затем, что признак хорошего кода это его гибкость, которая проявляется в т.ч. в том, что код можно трансформировать, инлайнить, итд, да хоть вовсе от него избавиться.

P>·>Если при этом нужно плодить копипасту, то это явно что-то не то. И неясно причём тут инлайн и гибкость кода. str.length() — это негибкий код?!
P>Вы сейчас перевираете.
Неперивираю, ты просто свои слова забыл. Напоминаю: " гибкость, которая проявляется в т.ч. в том, что код можно трансформировать, инлайнить". А дальше чистая логика:
"гибкий код → можно инлайнить" => "инлайнить нельзя → не гибкий код" см. https://en.wikipedia.org/wiki/Contraposition

P>Речь про инлайн вашего nextFriday(). Об ваш супер-крутой подход почему то ИДЕ спотыкается и вы проблемы в этом не видите.

Я спрашиваю "зачем инлайн"? Какую задачу-то решаем этим инлайном?!

P>>>Вы так и не рассказали, как будете решать ту самую задачу с фильтрами.

P>·>Рассказал и даже код показал,
Автор: ·
Дата: 28.12.23
насколько понял твою задачу. Если ещё остались вопросы, спрашивай.

P>Ваши тесты проверяют только тривиальные кейсы, на которых все зелено. В проде как правило кучка всего подряд.
Ну добавь эту твою кучку в тесты. Вопрос-то в чём?

P>>>И не надо.

P>·>Надо, если хотим тестировать поведение, а не детали имплементации.
P>Тестируйте базу, я ж не мешаю. Можете еще и операционку и джава рантайм сюда добавить
Я не базу тестирую, а запросы, которые строит мой код. А ты хз чего тестируешь.

P>>>Билдера запросов к базе данных. Вы что думаете, все sql запросы руками пишутся?

P>·>Да похрен как они пишутся, хоть левой пяткой. Это всё детали реализации. Главное что они работают ожидаемым способом.
P>Работают ожидаемым способом — это вы на примитивных фильтрах, что вы продемонстрировали, не проверите.
Ты так заявляешь, что продемонстированном этим кодом подходе нельзя написать фильтр какой тебе угодно непримитивный. С чего ты взял? Пиши фильтры какие придумаешь.

P>О том, что фильтр может вернуть много записей, вам может сказать или сам запрос, или его результат.

Как запрос может что-то сказать?

P>Второй вариант у вас отпадает, т.к. в тестах у вас не будет хрензнаетсколькилион записей в тестах

Зачем мне столько записей? В простом случае понадобится две записи — та которая я ожидаю, что она должна вернуться и та которая я ожидаю, что не должна вернуться.

P>>> Кое что, для аналитики — возможно. Но это большей частью всё равно перегоняется в орм или билдер. Есть орм — можно проверять построение логики запросов орм.

P>·>В смысле ты в своих тестах linq/etc тестируешь? Зачем? У linq свои тесты есть.
P>Это вы собираетесь базу тестировать
Опять врёшь. И цитаты, ясен пень, не будет.

P> и топите за поведение. А мне надо всего лишь построение запроса к орм проверить. Если орм не позволяет такого — надо смотреть его sql выхлоп, если и такого нету, значит придется делать так, как вы предлагаете.

"Надо" кому? Мы вроде говорим о тестировании ожиданий бизнеса. Ты правда считаешь, что пользователи ожидают определённый выхлоп sql?!

P>>>Запрос к базе должен соответсвовать бизнес-требованиям. Только проверяем мы это в т.ч. посредством метапрограммирования.

P>·>Что за бред? У тебя в FRD прописаны тексты запросов??! Прям где-то у вас есть бизнес-требование "select * from users where id=?" и "in: [fn1]"?! Приходит к вам клиент и говорит "а мне очень нужно чтобы out: [fn2], когда можете сделать?"
P>Что вас смущает? Запрос к бд должен возвращать именно те данные, что прописаны в бл или непосредственно следуют из бл, нефункциональных требований итд. Вас почему то тянет всё к абсурду сводить.
P>Кроме бизнес требований, у нас куча важных вещей — те самые нефункциональные. Их на порядок больше чем бизнес-требований.
P>И всё это нужно покрывать тестами, иначе любой дядел поломает цивилизацию
Ещё раз. У тебя в коде теста, в ожиданиях находится "in: [fn1]" и т.п. Как это соответствует "возвращать именно те данные, что прописаны в бл"? Где в бл вообще будет фигурировать in, fn1 и прочий select?

P>>>·>Цитату в студию или признавайся, что опять соврал.

P>·>Цитаты нет. Т.е. соврал.
P>Смотрите выше — вы фильтры предлагаете тестировать однострочными вырожденными кейсами. Как вы будете ловить те самые комбинации, не ясно.
Ну я не знаю твой код. Я разрешаю тебе писать кейсы двухстрочные и даже трёхстрочные!

P>>>Вы до сих пор не привели решение кроме "ищем в бд"

P>·>Я код
Автор: ·
Дата: 28.12.23
привёл. Что не устраивает-то??! "ищем" — это "find", английский язык знаешь?

P>Смотрите сами: assertThat(testSubject.find(new Filter.byEqual("Vasya"))).isPresent();
P>У вас testSubject это или репозиторий, который вы предлагаете с подключеной бд тестировать
Да.

P>Ни то, ни другое проблему с фильтрами не найдут.

Почему?

P>>>·>Куда смотреть? Поломал что?

P>>>Вам надо мок подфиксить, что бы время было той системы
P>·>Чё?
P>Смотрите — первая версия — вызываем бл прямо из контролера.
P>Ваши тесты — вы обмокали провайдер времени, передаете туда-сюда, включая тесты контролера
P>т.е. тесты контролера зависят от мока
Т.е. в тестах контроллера мы прописали ожидание, что время используется серверное, в соответствии с бизнес-требованиям.

P>Вторая — вызываем бл при чтении из очереди, при этом время должно быть клиенское.

Т.е. это значит, что _изменились_ бизнес-требования и теперь мы должны брать не серверное время, а клиентское. Ясен пень, это фукнциональное изменение и соответствующие тесты надо менять. Если у тебя ни один тест не упадёт, у меня для тебя плохие новости.

P>А контролер занят всего лишь тем, что кладет запрос в очередь.

P>А это значит, что ваши тесты контролера идут в топку, как и моки которые вы под это дело прикрутили.
В контроллере надо будет заменить использование systemClock() на eventTimestampClock() и поправить тесты, чтобы зафиксировать функциональное изменение.

P>>>Наша задача гарантировать что запросы соответствуют бизнес-требованиям. Что бы это гарантировать, я выбрал реализацию с метапрограммированием. Вы, очевидно, выбрали другое — гонять горстку данных к базе и обратно,

P>·>Покажи пример бизнес-требования.
P>зачем?
Чтобы понять какое отношение имеет метапрограммирование к тестированию ожиданий бизнес-требований.

P>>>и у вас кейс с вырожденным фильром до сих пор не покрыт тестом.

P>·>Я не видел этого кейса, и не могу нафантазировать какие у тебя возникли проблемы его покрыть.
P>А он и не виден, т.к. проблема тупо в количестве вариантов которые могут порождать фильтры. Та самая data complexity.
Ну а в телепанию я не умею.

P>>>Не знаю, что у вас с этим за проблема, тут рукописные логи годами ходят в одном и том же виде, тригерят эвенты, и ничего. А гарантировать сохранность стиля генерируемого кода почему то вам кажется рокетсаенсом.

P>·>Да причём тут стиль? Это мелкий чих. Я могу переписать например "SELECT ... X OR Y" на "UNION SELECT ... X ... SELECT ... Y" для оптитмизации. У тебя все тесты посыпятся и любая ошибка — увидишь после деплоя и прогона многочасовых тестов, в лучшем случае (а скорее всего только в проде). А у меня только те тесты грохнутся, если я реально ошибусь и что-то поломаю в поведении, ещё до коммита.
P>Очевидно, посыплются не все. Ну нет такой ситуации что бы билдер на каждый запрос лепил UNION. Отвалится скорее всего несколько кейсов, ради которых это все и затевается.
Ну этот кусочек может быть в большой группе запросов, выбирающих разные данные по-разному, но часть логики — общая.

P>>>Потому и стоит менять дизайн что бы это контролировать, а через di протаскивать только технологические зависимости.

P>·>Зато этот код очень простой, линейный и на 99% покрывается компилятором.
P>Не проверяется. Ваш CalendarLogic может принимать разные экземпляры — с текущим временем, клиентским, серверным, из параметра, итд. Всё что вам компилятор скажет, что типы нужные.
P>Ну и что?
P>Кто даст гарантию, что в проде вы правильно прописали зависимости для CalendarLogic? Интерфейс то один и то же
Гарантию, конечно, никто не даст, но покрыть тестами с моками тоже можно. Для этого моки и придумали.

>> Остальной 1% покрывается стартом приложения.

P>Еще меньшая гарантия.
Опять ты о гарантиях заговорил, опять опозоришься.

P>>>Очень просто — у нас уже есть тесты, одни должны сохраниться без каких либо изменений.

P>·>Я спросил как сделать так, чтобы такие тесты были, ты ответил
Автор: Pauel
Дата: 31.12.23
"Никак.". Т.е. тестов у вас таких просто нет.

P>Вы вопрос свой прочитать в состоянии? как именно ты собираешься в "curl" и-тестах ассертить
P>Буквально curl тест все что может, это сравнить выхлоп, и всё. Если там время не фигурирует, то никак.
Ну, допустим, фигурирует. И чем это тебе поможет?

P>Если же вы имеете ввиду интеграционные, то будет все ровно как и у вас.

С моками? Ну дык об чём спор-то тогда? Ведь именно моком я могу протащить тестовые значения now в нужные места. Там, где ты предлагаешь Time.Now синглтон, в который ты ничего протащить не сможешь.

P>·>Я о чём и говорю. В каких-то случаях у тебя могут меняться запросы. Ты уже сам говорил о случае с пустым значением, которое ты заявил, что и-тестами тестировать не собираешься.

P>·>И это могут быть нефункиональные требования, а какие-то технические детали, оптимизации. Простой пример. Скажем, если идёт выборка по небольшому списку параметров, то строится запрос с "IN (?,?)", иначе создаётся временная табличка и с ней джойнится.
P>Бинго! Оптимизация, нефункциональные требования, технические особенности — все это имеет чудовищное влияние, намного больше, чем сама бизнес-логика.
P>Если вы потеряете оптимизацию — приплызд. и тест нужен зафиксировать эту особенность что бы сохранить при изменениях.
Это уже будет перф-тест и к обсуждаемой теме не относится. Я пока обсуждаю именно и/ю-тесты. И суть в том, что твоё "интеграционные тесты строятся по функциям, а не по запросам к базе" — несостоятельный подход. Т.е. в бизнес-требованиях будет одна функция "выбрать по списку", следовательно и-тест у тебя тоже один, а то что от размера списка может срабатывать разный код, генеряться разные запросы и т.п. — это ты просто не протестируешь и будешь ловить проблемы на проде.

P>>>А вот запросы к базе вещь слишком сложная и ответственная, потому стоит использовать разные инструменты, а не рассказывать, что вам запретили метапрограммирование

P>·>Именно. Поэтому надо их выполнять и проверять результаты, неважно мета там или фета. Никто вам ничего не запрещает, а мой поинт в том, что важно что запросы делают, а не какие инструменты используются.
P>См выше — все нефунциональные вещи нужно проверять и фиксировать тестами.
Это не оправдывает твой подход, что функциональные тесты должны тестировать метапрограммирование.

P>>>Видите — не так, как вы привыкли и сразу ужос-ужос-мрак-и-смэрть

P>·>Ну вроде это уже вс проходили и забили. Заталкивать тесты в прод-код... зачем??!
P>Затем, что это быстрый, дешовый и надежный способ узнавать о проблемах с приложением в целом
P>Покажите, как вы все эти кейсы обнаруживать будете.
Возвращаемся к старому спору. Я уже отвечал на это неоднократно: системы мониторинга, health probes и прочие операционные инструменты. Это не тестирование.

P>Полагаю ответ будет "у нас багов на проде нету" ?

Вот таких багов "протух-обновился сертификат" — и не должно быть. Такие вещи именно мониторингом отслеживаются заранее, чтобы пофиксить можно было до возникновения этой ситуации. А у вас серты протухают ВНЕЗАПНО, и обнаруживаются только тестами постфактум, что у вас в проде что-то где-то отвалилось... ну могу только пособолезновать вашим клиентам.

P>>>·>А конкретнее? С примером кода.

P>·>Именно. Примера кода я так и не дождусь.
P>насколько я понимаю, это я все еще жду вашего примера про фильтры, который решает ту самую data complexity
Какую ту самую? Я никакой "data complexity" не видел и телепатией не обладаю, чтобы выдать какое-то решение для задачи о которой я не слышал.

P>>>Это и есть тесты билдера. Только конкретного, в котором заложено много различных вырожденных кейсов. linq не в курсе, что вы вытаскиваете всю базу, ему по барабану. Мне — нет.

P>·>Linq выдаёт список результатов — вот и проверяй, что в этом списке — вся база или не вся.
P> Вход на фильтры полным перебором подавать или магией воздействовать?
Да так же, как у тебя. Только ассертить не буквальный текст запроса, а результат его выполнения.

P>>>Ну так найдите эту комбинацию. А их, мягко говоря, немало. Как будете искать?

P>·>Анализом требовний, кода, данных, етс. Обычный corner case analysis. А как вы будете искать?
P>Вот-вот. Анализом. Только результаты нужно зафиксировать тестами таким образом, что бы следующиее изменение не сломало всё подряд втихую.
Да, верно.

P>>>И вы так прямо и втащите coq в любом проект на джаве?

P>·>Нет. А вы таки втащили доказательное программированипе в js?
P>Зачем? Доказывать свойства можно и без coq, только результаты нужно выражать в дизайне и подкреплять тестами.


P>>>Еще раз — фильтр не хардкодится, а строится.

P>·>Я потерял суть. О чём речь вообще? Пример в студи. КОД!
P>Вы когда нибудь видели дашборды где фильтры можно выбирать хрензнаетсколькилионом кнопочек и полей ввода?
Насколько мне доводилось видеть, они обычно через & соединяются. Т.е. список независимых фильтров. Сложность возникает когда фильтры между собой взаимосвязаны, но обычно такое избегают, т.к. юзерам такая compexity тоже не под силу, и использовать нормально всё равно не получается.

P>>>Нет, не называется. Разница между code-first и design-first не формате и расширении файла, как вам кажется, а в очередности деливери.

P>·>Верно. В design-first деливерится манифест first.
P>Не просто манифест — а любое описание, абы ваши пиры могли его понять и цикл разработки был быстрым.
Что значит за любое описание? Манифест это и есть описание api.

P>>>В том то и дело — ваша задача накидать интерфейсы-модели так, что бы имплементации было 0. Тогда можно деливерить сразу, а вот будет ли деливериться имплементация, и когда — уже дело десятое.

P>·>Ну да. Откуда же берутся конфиги/тенанты/http-запросы?
P>Появятся после того, как вы заделивирите АПИ, например спустя год, когда у вас уже версия 1.x. И вот здесь вы решите перейти на rust, то в этот момент крайне странно топить за design-first — у нас уже готовое приложение версии 1.x, и нужно сделать так, что бы манифест отрендерился — с учетом тенантов и прочих вещей, включая not implemented. Или вы собираетесь бегать по отделам и рассказывать "а чо вы это делаете — мы же на джаве год назад это решили не делать" ?
У вас уже есть имплементация и мы вот наконец спустя год выкатываем манифест api, но ты всё ещё это называешь design-first?! Нет, это типичный code-first — когда прыгать надо, думать некогда.

P>>>Вы вместо уточнения пытаетесь в телепатию играть. Если у нас готовое приложение, то конечно же генерируется — и странно было бы иначе делать.

P>·>Угу. ЧТД. "готовое приложение" == "code-first".
P>Я вам говорю откуда возьмется апи в готовом, если вы решите уже готовое переписать на другом языке.
Причём тут design-first?