Здравствуйте, ·, Вы писали:
P>>Вы уже показывали, когда мокали репозиторий ради тестов контролера, а потом скромно замолчали на вопрос, что делать, если меняется дизайн
·>Вопрос я не понял, мол что делать если что-то меняется, ответил как мог — если что-то меняется, то эээ... надо что-то менять. Если тебя интересует конкретная проблема, сформулируй с примерами кода.
Вы мок затачивали на все тесты, или по моку на каждый?
1. На все — все ваши тесты неявно теперь зависят от мока. И если вы закостылили мок, то заточили тесты под синтетику, код — под такие тесты.
2. Если по моку на каждый — можно хоть ревью сделать. Кода больше, но и контроля больше.
P>>Я вам про функциональные требования, а вы мне про функции в коде. Проверка склейки — это вайтбокс тест уровня юнит-тестов. В интеграционных тестах мы проверяем ожидания вызывающей стороны. А её точно ничего про склейку не известно.
·>"следующая пятница текущего времени" — вполне функциональное требование. В интеграционных тестах ты прямо заявил, что никак не сможешь проверить это ожидание.
Проверка склейки — такого не будет. А вот поддерживает ли приложение такую функцию — нужно проверять интеграционным тестым.
И ваши моки не избавляют этого.
P>>Есть такая штука. От безысходности — если вы такой дизайн построили. Искусственное понижение уровня тестирования.
·>Ну да, верно. Ты так говоришь, как будто что-то плохое. Чем больше мы можем проверять низкоуровневыми тестами, тем лучше. "Искусственно" — тоже верно, мы режем зависимости там, где они самые тяжелые, требуют больше ресурсов, замедляют билд. "Безысходность" — потому что альтернативы — хуже.
Самосбывающееся пророчество — обычно безысходность очень быстро наступает в том случае, когда изначально заложились на моки b монолитный дизайн.
Вариантов дизайна всегда больше одного
P>>Наоборот — моки это инструмент тестирования тяжелого, монолитного кода перемешанного с зависимостями. Например, когда физически невозможно выделить логику в простые вычисления.
·>Что за "физически"? Тезис Чёрча знаешь? Любой код можно переписать на чистый, хаскель неплохо это доказывает. Вопрос — накой?!
Есть же пример Буравчика. Вам мало, надо к абсурду свести?
P>> Тогда приходят на помощь моки — как вы сами и сказали, замокали один компонент, что бы проверить остальные два.
·>Замокали "плохой", тяжёлый компонент, требующий файловой системы, сети, многопоточки, чтобы хорошо и легко проверить остальные два на саму логику и бизнес-требования.
В стиле ТДД — написали тесты на моках, а потом и дизайн вышел соответствующим
P>>Или как у Буравчика — мутабельные вычисления, кучка зависимостей да два ифа.
·>Ну "правильного" дизайна ты предолжить просто не смог. Т.к. это более менее реальный проект с нетривиальной бизнес-логикой. А твои штуки вменяемо работают только на хоумпейджах.
Какой еще "правильный"? Я продемонстрировал, что минимальными усилиями можно свести тесты к таблице истинности и хранить все это максимально близко к коду.
P>>И если вы предпочитаете моки, то и дизайн кода будет тяжелым.
·>Я не знаю что за такое "тяжелый дизайн".
Это тот, который вы демонстрируете
·>Связывание находится в wiring code. Контроллер это просто ещё один компонент, ничем принципиальным от других не отличается.
Связывание и есть wiring код. Основная обязанность контроллера — именно это. И тестировать связку, как следствие.
Если же вы сторонник жирных контроллеров, конечно же вам придется мокать всё на свете. Не только time, а еще и sin и cos
P>>Ы-ы-ы — всё это придется затыкать юнит-тестами, компилер вам не помощник.
·>Покажи код где там какой типизации не хватает в java. И не просто общие слова, а не забывай про контекст обсуждения.
Я ж вам перечислил. Вы хотите еще и фичи статической типизации докинуть к нынешней теме?
P>>blackbox — тестируем исходя из функциональных требований.
·>И? "текущее время" vs "данный момент времени" — это функциональные требования. Причём тут вызовы функций?
Вы тестируете, что результат зависит от конкретного вызова. Сами же про это и пишете.
P>>Вы сейчас в слова играете. Если не протаскивать зависимости, в CalendarLogic будет null во время вызова, буквально. Это значит, вы всю мелочевку повесите на dependency injection.
·>Что за ерунда? Откуда там null возьмётся? Что за "повесите на dependency injection"? источник времени — это зависимость CalendarLogic, инжектится через конструктор. ЧПФ, забыл опять?
Сами спросили — сами ответили.
P>>А вот вам кейс — трохи поменяли архитектуру, и БЛ начала вызываться через очередь, с задержкой от часов до дней. А время должно отсчитываться с момента когда юзер нажал кнопку.
·>Поменяется InstantSource, будет добывать время не из system clock, а из таймстампа в очереди. Это вообще никакое не архитектурное изменение, а одна строчка в wiring code — что инжектить как источник времени.
О — InstantSource появился. Значит инжектиться может не один единственный вариант с Now, а минимум два. Где у вас тестирование инжекции? А ну как на проде все инжектиться будет по старому?
P>>В моем случае всех изменений — вместо прямого вызова делаем отложеный.
·>Что за отложенный вызов? Вызов где?
А как вы понимаете, что такое отложеный вызов? Недавно вам непонятно было что такое dependency injection, потом выяснилось, что все вам известно. Тут похоже вам снова надо с самим собой поговорить
P>>Интересно, что может быть и так и так — часть вещей считаем сейчас, часть — потом, а время используется и там и там, как текущее, так и предзаписанное.
·>Ну будет два InstantSource в разных местах.
Вот-вот. Как вы отличите что первый и второй не перепутаны? Это ж ваша любимая тема — надо проверить, что передали то самое!
P>>Это вам пример, как изменение дизайна вносит хаос именно в моковую часть тестов.
·>В моках надо будет просто сделать два источника времени, у и ассертить, что в нужных местах используется время из ожидаемого источника.
А еще можно обойтись без моков
И свести к таблице истинности, поскольку интеграционный тест всё равно понадобится.
P>>Ога — каким чудом компилер узнает, что в отложеном процессинге время в той самой функции надо брать с момента инициирования запроса, а в аудите и то, и другое?
·>Это будет две функции. ЧПФ, слышал?
Я в курсе: "ассертим что вызываем с тем параметром а не этим"
P>>Ровно там, где у нас связывание, а таких мест
P>>1 немного
·>У тебя много, на каждый сценарий.
Не всё так страшно — мы упрощаем не только бл, но и связывание. Посколько у вас больше одного провайдера для зависимости, всё может играть по разному в зависимости от сценария.
В одном сценарии у вас один провайдер времени, в другом — другой. Опаньки! Только вы это размазали по коду.
P>>2 код в них тривиальный, линейный
·>Да, но кода этого много накопипасчено.
Ну и что?
P>>Вы только что говорили о чудесной поддержке иде именно вашего дизайна. Покажите это чудо. Мне вот ИДЕ спокойно может заинлайнить любое из мест вызова nextFriday(now)
·>Круто, но зачем? Задачу-то какую решаем?
Затем, что признак хорошего кода это его гибкость, которая проявляется в т.ч. в том, что код можно трансформировать, инлайнить, итд, да хоть вовсе от него избавиться.
·>Не с любыми, а только с теми, которые тестируют детали имплементации, а не логику. Поэтому я и говорю, что _эти_ ю-тесты — не нужны. Ибо всё ровно то же придётся покрывать и-тестами с субд. Ты можешь объяснить зачем ты пишешь такие ю-тесты? Повторюсь, эти тесты не только бесполезны, но и вредны, т.к. хрупкие и усложняют внесение изменений.
Вы так и не рассказали, как будете решать ту самую задачу с фильтрами.
P>>Для того нам и нужна таблица истинности рядом с кодом и его тестами — такое можно только другими методами проверять
·>Дело в том, что в случае субд у тебя кода субд нет.
И не надо.
P>>Это выхлоп билдера. С чего вы взяли, что такой код надо руками выписывать?
·>Это какой-то небуквальный выхлоп или что? Выхлоп какого билдера?
Билдера запросов к базе данных. Вы что думаете, все sql запросы руками пишутся? Кое что, для аналитики — возможно. Но это большей частью всё равно перегоняется в орм или билдер. Есть орм — можно проверять построение логики запросов орм.
P>>Соответсвенно, и тестировать нужно соответствующим методом
·>Ты вроде сам заявлял, что тестировать нужно ожидания. Какое отношение метапрограммирование имеет к бизнес-требованиям или ожиданиям?
Запрос к базе должен соответсвовать бизнес-требованиям. Только проверяем мы это в т.ч. посредством метапрограммирования.
P>>Ваша альтернатива — забить на data complexity, так себе идея.
·>Цитату в студию или признавайся, что опять соврал.
Вы до сих пор не привели решение кроме "ищем в бд"
P>>do_logic и есть или юз-кейс, или контроллер.
·>Я имею в виду вот этот твой do_logic. Это не юзкейс. Это код-лапша.
Сравнивать нужно не как выглядит, а всё цельные решения, включая моки всех сортов, dependency injection, размазаную и скрытую, тесты которые без таблицы истинности итд.
P>>>>Я и говорю — телепатия. Вы лучше меня знаете, что у меня в коде, что у меня за проект, где больше всего проблем возникает, сколько должен длиться билд
P>>·>Ты вроде прямо заявил, что некий аспект поведения ты не можешь протестировать. И время билда ты сам рассказал. Я телепатией пользоваться не умею, могу привести твои цитаты.
P>>Все аспекты никто никогда не тестирует, у вас солнце погаснет раньше. Выбирая стратегию тестирования мы решаем что самое важно, а что нет.
·>Ты читать умеешь? Я пишу "некий аспект", ты возражаешь "Все аспекты". Я где-то требовал тестировать все аспекты, кроме как в твоих фантазиях? Я спросил как тестировать один конкретный тривиальный аспект, который элементарно тестируется с моим дизайном, а ты не смог, у тебя солнце погасло.
У вас плохо с логикой — если нет возможности тестировать все аспекты, а это невозможно, значит в каждом конкретном случае какие то аспекты будут непротестированы.
Ваш тривиальный аспект это "вызываем с нужным параметром" ?
о.
P>>·>Я топлю за вайтбокс в том смысле, что тест-кейсы пишутся со знанием что происходит внутри, чтобы понимать какие могут быть corner-cases. Но сами входные данные и ассерты теста должны соответствовать бизнес-требованиям (для данного id возвращается правильно сконструированный User). Следовательно, изменение имплементации всё ещё должно сохранять тесты зелёными.
P>>Нет, не должно.
·>Должно, т.к. это тестирование бизнес-требования.
Должно, только ваш nextFriday поломал вообще всё, т.к. ему надо прописать другие значения, а без этого или все тесты красные, или зелены при гарантировано нерабочей системе.
P>>Смотрите про тривиальный перенос вызова из одного места в другое — ваш nextFriday поломал вообще всё
·>Куда смотреть? Поломал что?
Вам надо мок подфиксить, что бы время было той системы
P>>·>Вот твой код "{sql: 'select * from users where id=?', params: [id], transformations: {in: [fn1],out: [fn2]}" — таким свойством не обладает, т.к. проверяет детали реализации (текст запроса, порядок ?-парамов, имена приватных функций), а не поведение. Этот тест будет ломаться от каждого чиха.
P>>Кое что сломается. Поскольку это метапрограммирование, то это ожидаемый результат.
·>Ожидаемый кем/зачем?
Наша задача гарантировать что запросы соответствуют бизнес-требованиям. Что бы это гарантировать, я выбрал реализацию с метапрограммированием. Вы, очевидно, выбрали другое — гонять горстку данных к базе и обратно, и у вас кейс с вырожденным фильром до сих пор не покрыт тестом.
P>>Что бы от каждого чиха — такого нету. Запросы меняются с изменением бизнес логики.
·>Я тебе приводил пример — заменить звёздочку на список полей — это чих, а не изменение бизнес логики.
Не знаю, что у вас с этим за проблема, тут рукописные логи годами ходят в одном и том же виде, тригерят эвенты, и ничего. А гарантировать сохранность стиля генерируемого кода почему то вам кажется рокетсаенсом.
P>>В интеграционных тестах мы не проверяем точки — мы проверяем саму сборку.
·>Если сборка идёт в трёх точках — проверить легко. Если сборка идёт в трёхсот точках — проверить сложно.
Вы почемуто забываете про dependency injection — это такой код, который труднее всего покрыть тестами, а между тем у него чудовищное влияние на систему.
Потому и стоит менять дизайн что бы это контролировать, а через di протаскивать только технологические зависимости.
P>>То, что вы пишете — именно вайтбокс тест, тавтологичный "проверим что подставилось время клиента" — это и есть суть проблемы.
·>Почему вайтбокс? Тест бизнес-требования — "в ответе приходит правильное время".
Если это про некоторую сборку, когда вы проверяете результат, то все годится. Только вам здесь надо вбросить "время клиента" каким то раком. А потом проверить, что вбросили удачно.
P>>Вызовем как то иначе, но обеспечим результат тот же, ваш тест сломается т.к. он по прежнему будет долбить "проверим что подставилось время клиента"
·>Как ты _проверишь_, что результат тот же и соответствует ожиданиям?
Очень просто — у нас уже есть тесты, одни должны сохраниться без каких либо изменений. Раз мы их спроектировали, а не наструячили от балды, убедились в этом, то они рассматриваются, как корректные.
Теперь изменение реализации должно сохранить тесты зелеными
·>·>·>Как ты тестируешь что построенный запрос хотя бы синтаксически корректен?
·>тот самый интеграционный тест, на некорретном запросе он упадет
·>Т.е. у тебя есть туева хуча ю-тестов для каждого сценария, которые проверяют _тексты_ запросов, но для проверки, что текст запроса хотя бы корректен — приходится ещё и и-тестом каждый запрос проверить для ровно тех же сценариев.
интеграционные тесты строятся по функциям, а не по запросам к базе.
А вот запросы к базе вещь слишком сложная и ответственная, потому стоит использовать разные инструменты, а не рассказывать, что вам запретили метапрограммирование
P>>Для юнит-тестов это как раз нормально, их предназначение — тестировать всю мелочевку, из за которой у вас вообще что угодно можно происходить
·>Дело не в мелочёвке-большичёвке, а в том, что ты тестируешь код как написано, а не ожидания/бизнес-требования. Ровно то, чем ты меня пытался попрекать.
Похоже, что вам действительно ктото запретил метапрограммирование
P>>Во многих фремворках для тестирования вы юнит-тесты можете размещать прямо в том же файле, или вообще привязывать аннотациями.
·>Мрак.
Видите — не так, как вы привыкли и сразу ужос-ужос-мрак-и-смэрть
P>>>>Ну как же — вы собирались фильтры тестировать посредством выполнения запросов на бд, те самые "репозитории" которые вы тестируете косвенно, через БД.
P>>·>Да.
P>>Вот вот.
·>Ты так говоришь, как будто это что-то плохое.
Хорошего в этом как то маловато.
P>>И как же вы решаете проблемы с data complexity которая тестами не решается используя исключительно косвенные тесты?
·>А конкретнее? С примером кода.
Это я вам вопрос задал, вообще говоря. Те самые фильтры — вполне себе хороший пример.
P>>Это выхлоп билдера — в нем связывание, конверсия, маппинг и тд. Т.е. у нас кейс с метапрограммированием — результат фукнции это код другой функции. И я использую ровно тот же подход что и везде — сравнение результата. Только в данном случае наш результат это код.
·>Зачем нам тестировать выхлоп билдера? У билдера должны быть свои тесты.
Это и есть тесты билдера. Только конкретного, в котором заложено много различных вырожденных кейсов. linq не в курсе, что вы вытаскиваете всю базу, ему по барабану. Мне — нет.
P>>Например — фильтр вырождается в true. А вот если мы накладываем ограничения на результирующий запрос, у нас эта часть исключается принципиально.
·>Не понимаю проблему. Если фильтр выродится в true, то субд вернёт те записи, которые мы не ожидаем, тест провалится.
Ну так найдите эту комбинацию. А их, мягко говоря, немало. Как будете искать?
P>>Простой вопрос был — как ваше доказательство проверит само себя если другой коллега начнет писать код как ему вздумается?
·>Верификатор выдаст ошибку. Почитай про какой-нибудь coq что-ли.
И вы так прямо и втащите coq в любом проект на джаве?
·>Ну будет в коммите одном из десятка тестов вырожденный фильтр... И? Особенно весёлые штуки типа "A = B OR A <> B" из-за немножко не так поставленных скобочкек. Ты как равьювер будешь смотреть тесты и в уме интерпретировать sql запросы?
Еще раз — фильтр не хардкодится, а строится.
P>>Наоборот. Руками затейники выдают такое, что ни один генератор проглотить не может. А если вы начинаете с аннотаций или хотя бы интерфейсов, то уж точно ясно, что это реализуемо.
·>Верно, но называется это code-first со всеми последствиями.
Нет, не называется. Разница между code-first и design-first не формате и расширении файла, как вам кажется, а в очередности деливери.
P>>Наоборот. АПИ то никуда не делся, только работаем мы с ним немного не так, как вы привыкли. Тесты всё равно остаются. И их надо не ногой писать, а согласно определению апи. И документирование, и клиентский код, и много чего еще.
·>Апи будет просто набор интерфейсов и/или модели данных. С нулевой логикой.
В том то и дело — ваша задача накидать интерфейсы-модели так, что бы имплементации было 0. Тогда можно деливерить сразу, а вот будет ли деливериться имплементация, и когда — уже дело десятое.
Посмотрите grapql, grpc — API и есть код, вопрос в том, что вы деливерите. Полурабочий каркас приложения — это code-first. А если описание, которое могут взять другие команды, и работать независимо — design first
P>>Вы вместо вопроса слишком злоупотребляете телепатией. АПИ, если отдаётся, то в большинстве случаев уже ничего генерить не надо. Например, согласовали интерфейс, зафиксировали, и каждый пошел работать над своей реализацией.
·>Ты опять врёшь. Не телепатией, а из твоих слов: "Манифест генерируется по запросу, обычно http", противоречие выделил жирным. Или ты похоже просто в прыжке переобуваешься.
Вы вместо уточнения пытаетесь в телепатию играть. Если у нас готовое приложение, то конечно же генерируется — и странно было бы иначе делать.
А если у нас ничего нет, только АПИ, то пишите АПИ сразу так, что бы быстрее его заделиверить и цикл разработки был максимально короткий.
P>>Вы похоже не вдупляте — design-first = вначале деливерим АПИ, а потом уже реализация, куда входят переменные, конфиги, логика тенантов итд.
·>http-сервер это такой АПИ... Угу-угу.
Я вам про интерфейсы, а вы сюда http-server тащите. Дурака валяете.