Здравствуйте, Pauel, Вы писали:
P>·>Не экономит. Конкретные типы и конструкторы с ними соответствуют конкретным арифметическим операциям с календарём. Твоё toFields — какое-то месиво, неясно о чём и чревато багами. Скажем, если у тебя тип time окажется какого-то не того типа, то эта твоя ... начнёт давать странное месиво с неясным результатом из=за перекрытия одноимённых полей. Не надо так делать.
P>Ваша альтернатива — понасоздавать вагон методов для конверсии "всё во всё" что дает вагон кода и вагон тестов. Или же вместо однострочной конверсии у вас будет несколько строчек кода точно так же без полного контроля.
Моя альтернатива аккуратно реализовать реальную модель календаря с аккуратной арифметикой различных типов. Конверсия "всё во всё" — это полное непонимание предметной области.
P>·>Недостаточно для чего? Почему недостаточно? Причём тут таблица истинности?
P>Логику, вычисления мы тестируем по таблице истинности. И чем меньше дополнительного кода сверх этой таблицы истинности, тем лучше. А вам надо все поразмазывать по мокам
Ну тестируй. Моки тебе ничем не мешают.
P>>>Вы уже показали — кода в тестах в вашем исполнении больше.
P>·>Больше, т.к. код делает больше.
P>Функционально — ровно столько же.
Нет. Он у меня ещё и содержит склейку. У тебя склейки нет.
P>>>У вас какая то мешанина — интерграционные это все что выше юнитов. Сценарии — это на самом верху, уровень приложения или всей системы. Не ясно, за счет чего у вас будет меньше интеграционных — похоже, вы верите, что моки здесь чего то решают.
P>·>Меньше интеграционных за счёт того, что они тестируют интеграцию. А не всю копипасту, которую ты предлагаешь плодить.
P>Моки не тестируют интеграцию. Моком вы можете проверить, а что будет, если подкинуть ту или иную зависимость. А нам надо совсем другое — готовый компонент работает как нам надо.
Ты читать умеешь? Ты где увидел в моём предложении выше слово "моки"? Хинт: моки вообще ничего не тестируют. Тесты — тестируют.
P>Количество интеграционных тестов моки уменьшить никак не могут. Моки — это фактически понижение уровня тестирования за счет обрезания зависимостей. Нам же надо не понижение, а повышение.
Могут, т.к. большинство логики тестируется ю-тестами. А и-тесты нужны только чтобы тестировать интеграцию уже протестированных компонент.
P>>>Это ж не значит, что и применять данных подход нужно везде. В некоторых интеграционных тестах, которых обычно немного.
P>·>Как немного? Ты пишешь по интеграционному тесту для каждого do_logic. Т.к. именно там происходит склейка.
P>Есть метод — его нужно вызвать в тестах. И у вас будет ровно то же. Просто вас тянет тестировать код, а не ожидания вызывающей стороны.
У меня такого метода просто не будет. Следовательно и тестов писать просто не для чего.
P>Склейка или нет дело десятое — вызывающая сторона никаких ожиданий на этот счет не имеет, ей про это неизвестно.
P>Проверка конкретной склейки — это вайтбокс, тест "как написано", классика тавтологии.
P>Вместо проверки конкретной склейки делаем интеграционный код линейным, буквально, а интеграцию осуществляем на самом верху.
Чем выше, тем больше мест. Функция nextFriday() осуществляет склейку у себя внутри. И покрывается одним тестом. У тебя будет nextFriday(now) — и на самом верху будет множество call site для этого накопипастено. Код линейный, да, но накопипастен в кучу мест.
P>>>deep equals проверяет значение, всё разом, если у вас чтото больше чем инт или строка
P>·>Я знаю. И это плохо, особенно для юнит-тестов.
P>Простите, вы мапперы из одной структуры в другую как тестировать собираетесь?
В ю-тестах — по частям. Иначе изменение логики маппинга одного поля сломает все deep.equal тесты, где это поле есть.
P>>>Поломаются тесты не там, где вы обмокали, а ровно по месту — меняется таблица истинности функции
P>·>Так функция может использоваться из множества мест.
P>Пусть используется. Что вас смущает ?
Что в каждое из этих мест тебе понадобится копипастить склейку nextFriday(now) с источником текущего времени и покрывать это ещё одним тестом. Хотя, как я понял, ты вообще заявил, что такое протестировать ты просто не сможешь.
Ещё раз.
nextFriday() ≡
nextFriday(now) ∘
timeSource.now(). Если ты отказываешься использовать в call site готовый и протестированный
nextFriday(), значит тебе в каждый call site придётся протаскивать timeSource и копипастить композицию с
nextFriday(now). Это увеличивает кол-во прод-кода и требует больше более тяжеловесных тестов для покрытия всей копипасты.
P>>>В вашем случае далеко не факт, что найдете в моках все нужные места, и сделаете что бы соответствовало
P>·>Какие места? Зачем их искать?
P>Читайте себя "Так функция может использоваться из множества мест."
И?
P>У вас в тех самых тестах все будет обмокано. Как узнаете, что там, в моках, должно вызываться не "то", а "это"?
Эээ.. Ты рефакторингом точно умеешь пользоваться? Компилятор и IDE всё покажет, подскажет и подправит.
P>>>Если у вас будет подсчет статистики, вы вспотеете покрывать тестами "из базы"
P>·>Ты потерял контекст. Тут шла речь о ю-тест упоминал "а что если в колонке пусто". В колонке, внезапно окажется, будет не пусто а дефолтное значение из бд. Багу такую поймаешь только на проде, т.к. и-тесты для таких подробных мелочей явно писать не будешь.
P>Наоборот — именно для таких мелочей юнит-тесты и нужны.
Ты не читаешь, что тебе пишут.
P>·>Я эту багу поймаю на и-тесте repo+dbms — пытаясь воспроизвести пустую колонку.
P>Вы пытаетесь таблицу истинности запихать куда подальше, в данном случае в бд. А она должна быть прямо рядом с тестом как можно ближе к коду.
P>Что там будет с бд, когда другой девелопер чего нибудь фиксанет — а хрен его знает.
P>А если таблица истинности рядом с тестами и тестируемой функцией, шансов намного больше что проблему заметят еще во время код ревью.
Нет, не пытаюсь, ты опять фантазируешь. Я пытаюсь донести мысль, что надо проверять что код делает, а не как он выглядит.
P>>>Не дополнительными, а теми же самыми интеграционными, что есть и у вас. Интеграционные тесты не пишутся на все вещи вида "проверим что где то внутре передается параметр". Если такое критично — выносится в юниты.
P>·>Ты не в тему споришь. Ты заявил "Интеграционный тест проверяет 1) что сама склейка валидная". Мест склейки now и nextFriday(now) у тебя будет полно. У меня такое место — одно.
P>Интеграционный тест проверяет всю интеграцию разом, а вас тянет проверить каждую строчку. Каждая строчка — это про юнит-тестирование.
Твой один интеграционный тест проверяет интеграцию одного do_logic. Таких do_logic у тебя будет дофига, и каждого надо будет проверить.
P>>>Если у нас код контролера линейный, а в моем случае это именно так, то нам нужен ровно один интеграционный тест, который нужен и вам
P>·>_Такой_ интеграционный тест мне не нужен. Я могу обойтись одним ю-тестом с моком о том, что nextFriday() действительно возвращает правильные даты для разных моментов времени. И ещё соответсвующими ю-тестами в местах использования nextFriday(), там можно моком просто возвращать хоть семь пятниц на неделе.
P>Какой такой? Вам всё равно нужен интеграционный тест. Задачи интеграционных тестов не каждую склейку проверять по отдельности, а проверять работает ли компонент собраный целиком. Что там унутре — для интеграционного тестирования по барабану — нужная функция работает, значит всё хорошо.
Интеграционный тест чего?
P>>>Никак.
P>·>Именно. Т.е. усложняешь прод-код, теряешь возможность протестировать и ловишь баги в проде. Нам такого не надо.
P>Забавная телепатия.
Никакая не телепатия. Если ты никак не можешь протестировать, то проблемы будут обнаруживаться в проде. Ок, возможно, ещё при ручном тестировании.
P>>>У вас причин для изменения гораздо больше — любое измение дизайна означает поломку ваших тестов на моках.
P>·>Это страшилки.
P>Это следует из ваших примеров.
Не следует.
P>·>Мде. Если хочешь строчки считать по честному, показывай изменения в прод коде и тест на каждый вызов nextFriday.
P>Тест на каждый вызов это классика тавтологичного тестирования
Я имею в виду писать тест, который покроет код каждого вызова и заасертит, что ожидания выполняются.
Я всё ещё жду требуемые изменения в прод коде для твоего дизайна.
P>·>Сейчас начнёшь символы считать, да?
P>Обычное правило — 1 инструкция на строку. Вы втюхали минимум две.
И что? Небо на землю упадёт?
P>А поскольку у вас значения захардкожены, то между разными тестами согласованность никак не проверяется, т.к. везде хардкодите значения.
Ну ввести константу — не проблема.
P>>>И не собираюсь — data complexity решается совсем другими инструментами. Количество перестановок в фильтрах спокойно может исчисляться чудовищным количеством кейсов. Если некто поправит запрос, и забудет нагенерерить сотню другую данных — приплыли.
P>·>Именно. Так что неясно зачем ты делаешь эти предъявы о гарантиях. У тебя аргументация вида "Раз ваш подход не умеет кофе варить и тапочки приносить — значит это плохо и надо делать как фаулер завещал".
P>Вы забыли, что именно вы претендуете что решите это всё тестами.
Опять фантазии. Давай в следующий раз когда ты решишь что я думаю, на что я претендую, и т.п., подкрепляй моими цитатами.
P>>>т.е. вы снова притягиваете решение которое зависит от человеческого фактора
P>·>А у тебя есть решение независящее от человеческого фактора?
P>Его можно ограничить контролируя построение запроса + таблицу истинности храним не абы где, а рядом. Вы же все хотите таблицу истинности или размазать, или запихать куда подальше.
Не знаю где ты собрался размазывать и запихивать таблицы, просто не делай так, я разрешаю. Но это не отвечает на вопрос, куда делся человеческий фактор-то?
P>>>тот самый интеграционный тест, на некорретном запросе он упадет
P>·>Т.е. у тебя интеграционные тесты будет тестировать всё "чудовищное количество кейсов"? Накой тогда ю-тесты?
P>Интеграционные тесты не проверяют варианты склейки — они проверяют функционирование в сборе. Юнит-тесты проверяют работоспособность примитивов.
У тебя плодится много точек сборки, которые теперь приходится проверять. Ты решил терминов наизобретать? Что такое "работоспособность"? Чем она отличается от функционирования?
P>·>Я это знаю из тригонометрии, от моего кода не зависящей. А вот то, что fn2 — правильно, а fn3 — неправильно ты знаешь только из "как в коде написано". Т.е. твой код тестирует самого себя, порочный круг называется.
P>Не из кода, а из требований вызывающей стороны.
Что за требования вызывающей стороны, которые диктуют, что должен быть написан код с fn2, а не fn3? Вызывающая сторона это вообще что и где?
P>>>fn1 и fn2 должны быть видны в тестах, а не публично
P>·>А тесты должны, по уму, тестировать публичный контракт. @VisibleForTesting — это очень хороший сигнал code smells.
P>Это ваша фантазия — эдак у вас юнит-тесты станут code smell, тк они принципиально по своей сути видят слишком много.
У нас юнит-тесты видят те же публичные методы, что и используются из других мест прод-кода. Тестировать то, что видно только тестам — это и есть твоё "как написано", завязка тестов на детали внутренней реализации.
P>>>Тем, что у вас в тестовой бд может не оказаться тех самых значений, которые ломают один из сотен-тысяч кейсов
P>·>И у вас в тестах может не оказаться тех самых значений для построения того самого ломающего запроса. Опять "тапочки не приносит"?
P>В моем случае таблица истинности рядом с тестом и функцией. Вы же ее пихаете куда подальше — прямо в бд.
P>Скажите честно — у вас есть требование по процессу во время код ревью обязательно смотреть содержимое тестовой бд? Те самые десятки-сотни-тысячи записей
Я не знаю что такое содержимое тестовой бд и накой оно вообще нужно. Я код теста
уже показывалАвтор: ·
Дата: 28.12.23
, хватит фантазировать. Делается save и тут же find для только что сохранённых записей. Я уже писал, что тысячи записей не нужны в подавляющем большинстве случаев. А там где нужны — создаются программно в этом же тесте.
P>>>Для этого вам надо перебрать все возможные сочетания фильтров -> здесь счет тестов идет на десятки тысяч по скромной оценке
P>·>Нам — не надо.
P>Вероятно, это особенность вашего проекта. Любая более-менее внятная статистика-аналитика по бд это довольно сложные алгоритмы, и здесь нужно чтото лучше, чем смотреть в бд и думать, что же должно быть на выходе.
Верно. Я уже показал что лучше — сохранить данные в бд и поискать их разными фильтрами.
P>>>Выборочные тесты. Поскольку я знаю, как строятся фильтры, мне нужно убедиться, что они таки вызываются. И не надо проверять всё перестановки.
P>·>Ага. Именно то самое whitebox, котором ты ещё недавно меня попрекал.
P>Раскройте глаза — здесь блекбокс. Выборочно вызываем бд нашим кодом, проверяется весь пайплайн в сборе, косвенно. Только нам не надо проверять всё на свете таким образом, т.к. есть и другие инструменты.
"я знаю, как строятся фильтры" — это и есть whitebox, не лукавь.
P>>>И вы уверены, что в тестовой бд всегда будет полный фарш? Вы что, один работаете?
P>·>"Тапочки не приносит".
P>Вы никак не можете объяснить преимущества размазывания таблицы истинности по тестами и бд, чем это лучше хранения таблицы истинности рядом с кодом и функцией с тестом.
Размазывание таблицы истинности придумал ты сам, сам и спорь со своими фантазиями.
P>>>Противоречие у вас в голове. Сказано — полного контроля не дают.
P>·>Именно. Зачем же тогда покрыть юнит-тестами?
P>-
P>Затем, что доказательство корректности — это необходимое условие. А такое доказательство + тесты + код ревью, необходимое и достаточное.
Если у тебя есть доказательство корректности кода, то тесты уже не нужны. Это и есть идея доказательного программирования.
P>·>И что мешает это же делать в repo+dbms?
P>Вы же сами говорите,что про это не думаете, верите только в результаты. Вот вы и нашли, кто мешает.
Опять мои мысли читаешь... Тебя не затруднит привести цитату, где я так говорю?
P>·>Это значит, что изменение в коде занимает как минимум день для выкатки в прод. Значит глобально архитектура выбрана плохо. Возможно надо резать на независимые компоненты.
P>Нет, не значит.
Ты сказал, что процесс билда и тестов занимает 2.5 часа. Это уже пол дня.
P>Вы не в курсе, ни что за проект, ни какие требования, итд. Далеко не все проекты могут протаскивать изменения в прод прям сразу.
Я знаю, но это обычно недостаток, а не повод для гордости.
P>Во вторых, я вам сказал, что это полная сборка, все платформы, всё-всё-всё. Кто вам сказал, что нет сборки отдельных модулей?
P>И так у вас всё — вместо вопросов занимаетесь телепатией.
Я задал вопрос — ты ответил 1.5ч + 40%, если надо, могу тебя процитировать, если ты запамятовал. Если ты умолчал важную инфу, релевантную для ответа на мой вопрос — это не моя вина.
P>>>Переписывать то зачем? Чем они вам мешают?
P>·>Тем, что они написаны на конкретном ЯП.
P>Я ж сказал — если по аннотации можно сгенерировать 4 артефакта, то почему нельзя сгенерировать 5й, 6й, 7й?
P>Вы же видите только переписывание
Вопрос в том, что является источником — прога на конкретном яп или language agnostic описание контракта.
P>>>·>Смотря как долго проект живёт.
P>>>Чем дольше, тем меньше шансов на переписывание, проще заново напилить новое.
P>·>Но при сохранении контракта, который у вас описан на аннотациях конкретного ЯП. В общем гугли ликбез, например, тут https://swagger.io/blog/code-first-vs-design-first-api/
P>Вы путаете code-first и design-first. design first — означает, что первое деливери это апи, а не реализация. code-first — наоборот. А какой файл вы в начале редактируете, json или java и yaml, дело десяток
Не десятое, а первое. Погляди в словаре что означает слово "first".
P>Я ж сразу сказал — апи уже есть, а реализации нету, зато можно и тесты писать, и клиентский код, и документацию прикручивать
А начался разговор, что у тебя аннотации хитрые и сложные, зависят от переменных окружения и погоды на марсе.