Здравствуйте, Pauel, Вы писали:
P>>>Вы и сами тащите "надо проверить old и new", и другого варианта не видите, как будто его нет. Можно же и дизайн поменять, что бы юнитами такое проверять. Но нет — вы топите за моки.
P>·>Есть, конечно, но другие варианты хуже. Больше писать кода и сложнее тестировать.
P>Наоборот — юнит тесты дешевле некуда.
И? С моками (стабами) тесты такие же дешёвые. Т.к. мы мочим всё медленное и дорогое. В этом и цель моков (стабов) — удешевлять тесты.
P>>>Потому, что вы используете дизайн, который плодит лишнее. Я вам на примере дата-время показал, как можно иначе, но вам любой пример не в вашу пользу надо забраковать.
P>·>По факту пока что кода лишнего больше у тебя. Притом "вот фаулер написал моки — плохо, давайте накрутим слоёв в прод коде!".
P>Это у вас телепатия разыгралась? С чего вы взяли, что у меня лишнего кода бОльше?
В статье кода стало больше. Вот эти все getUser — это всё лишний код в проде. Твои "{...toFields}" — тоже лишний код, и это всё прод-код.
P>>>Со статьё давно всё ясно. crypto api и time это пример, которые показывают, за счет можно отказаться от моков, и что будет, если этого не делать.
P>·>Ни для crypto api, ни для time моки не нужны обычно не потому что "дизайн", а потому что они быстрые, детерминированные, с нулём внешних зависимостей.
P>Вы сейчас сами себя отрицаете:
P>Во первых, вы сами недавно поделились решением для hmac — "моками проверим то и это"
Это твоё было решение. Я его просто сделал вменяемым.
P>Во вторых, чуть далее, тоже недавно, вы рассказывали, как моками отрезали Time.now
Ага. И это одна строчка кода. Ты взамен предложил _фреймворк_ для этого же.
P>>>Это у вас на моках упадут все тесты всех операций апи. О том и речь. А на обычных юнит-тестах вы подкидываете новый набор значений, и всё путём.
P>·>Почему десяток? У тебя только десяток операций в api? Откуда взялся десяток?
P>Десяток значений. Эти значения вы можете в каких угодно тестах использовать.
Значений чего? И почему они лежат в одном файлике?
P>>>"достаточно ассертить его наличие" — вот вам и привязка к "как написано"
P>·>Не понял. Наличие hmac — это ожидание, бизнес-требование.
P>Это не повод моки тащить.
Конечно, это я сразу тебе сказал. Моки для hmac — ты притащил. Перечитай топик, все ходы записаны.
P>Если значение hmac во всех кейсах совпадает с нашим, это куда лучшая проверка. Но вместо этого вы берете моки т.к. "это бизнес-требования"
Это плохая проверка. Т.к. при изменении формата/алгоритма этого hmac тебе придётся менять тестовые данные в этих самых всех кейсах.
P>>>Ага, тесты "как написано" Вам придется написать много больше, чем один тест — как минимум, пространство входов более-менее покрыть. Моками вы вспотеете это покрывать.
P>·>Я тебе показывал как надо моками покрывать.
P>Я про это и говорю — вы предлагаете писать мок на все случаи жизни.
Это твои фантазии.
P>Как убедиться что мокируемая система совпадает по поведению с вашим моком — не ясно.
Это не проблема моков. А проблема раздельного тестирования. У тебя ровно та же проблема: "Они конечно же должны быть согласованы".
P>>>Вы чего то недопоняли, но вместо уточнения начинаете валять дурака?
P>·>А толку уточнять. Я задал вопрос чего десяток — ответа так и не последовало.
P>Десяток значений, что очевидно. Что для time, что для токенов. Вот если вы тестируете свою либу date-time или hmac, там будет побольше значений, до сотни, может и больше.
Нет, совершенно не очевидно. И я всё равно не понял, какой time? каких токенов? какая своя либа?
P>>>time api это пример дизайна, как можно уйти от комбинаторного взрыва.
P>·>Это не "дизайн". Это если ЯП умеет в duck typing и сооружать типы на ходу. Впрочем, тут явно фича применена не по месту.
P>Это никакой не duck-typing. Это статически типизированые фичи — spread и destructuring. Если какого то филда не хватает — компилятор так и скажет. Если какой то филд будет иметь не тот тип — компилятор говорит об этом.
Насколько мне известно такое умеет только typescript из мейнстримовых яп.
P>>>Принципиальное отличе of вместо from
P>·>Принципиальное отличие что тут три разных типа Date, Time и Offset — и их никак не перепутать.
P>Запросто — local time и server time будут все равно Time, опаньки. Есть LocalTime, AbsoluteTime, итд. Ошалеете покрывать все кейсы
Я ничего не понял, но всё равно очень интересно. Если чё, то LocalTime/AbsoluteTime (это откуда кстати?) не просто так придумали, а потому что это действительно разные типы с разными свойствами, разной арифметикой и разными операциями. Это именно бизнес-область, календарь человеков — вещь такая, от которой если не достаточно ошалеть, будешь делать кучу ошибок.
P>·>Я предложил как решают задачу взаимодействия с неким абстрактным сложным API в тестах с моками (стабами?). То что это crypto — к сути дела не относится, я это сразу заявил в ответе на твой пример, перечитай. Так что это твои фантазии.
P>Вы показываете выбор в пользу моков, а не в пользу сравнения результата. Вы сами то это видите? Если у нас сотня кейсов в апи, ваш моковый подход превращается в тавтологию, тесты "как написано"
P>Эту самую сотню кейсов всё равно надо покрыть внятными тестами.
Я пытаюсь донести мысль, что пофиг — моки или результаты, это технические детали. Это ортогонально тестам "как написано", т.к. тесты это про семантику.
P>·>Суть в том, что компонент содержит несколько частей функциональности и весь компонент покрывается со всей его функциональностью, с моками (стабами?) зависимостей. После этого достаточно одного интеграционного теста, что несколько компонент соединились вместе и взаимодействуют хотя бы по одной части функциональности. Условно говоря, если в классе 20 методов и все 20 протестированы сотней юнит-тестов, то для тестирования интеграции обычно достаточно одного и-теста.
P>В том то и дело — я именно это вам пишу уже второй месяц. Только моки здесь играют сильно вспомогательную роль, их обычно немного, только там, где дизайн дороже править чем накидать один мок.
Нет, ты предлагаешь всё разделять на лямбды и писать кучу composition кода, который тоже весь приходится тестировать.
>> В качестве аналогии — когда ты вставляешь CPU в материнку — тебе достаточно по сути smoke test, а не прогонять те миллионы тестов, которые были сделаны на фабрике для каждого кристалла и конденсатора.
P>Недостаточно — полно причин, когда CPU проходит смок-тест. А вот загрузка операционки, что уже много больше смок теста, уже кое что гарантирует. И то полно случаев, когда в магазине пусканул систему, всё хорошо. Принес домой — комп уже не включется, а бибикает.
P>Вобщем, понятно, откуда вы берете невалидные идеи.
Даже если так. Загрузка операционки это один тест. Кристалл и каждый компонент современного компьютера на фабриках тестируют тысячами-миллионами тестов.
P>·>У тебя же будет дополнительных xxxComposition на каждый метод в прод-коде и это надо будет всё как-то тестииовать в полном сборе.
P>Вы там похоже вовсю фантазируете.
Вот это оно и есть:
{
load: () => repo1,
load2: () => svc,
... и так прокидываем все что надо
}
— эта лапша у тебя будет в каждом тесте, ещё такая же (почти!) копипастная лапша — и в прод-коде, которую придётся каждую покрывать отдельным и-тестом и это для каждого do_logic метода.
P>>>Кафка — это обычный эвент, как вариант, можно обойтись и простым юнитом, убедиться что функция возвращает объект-эвент.
P>>>Если у вас такой роскоши нет — добавьте мок ровно на нотификацию, а не на кафку.
P>·>Так вызов метода — это и есть по сути эвент. По крайней мере, изоморфное понятие.
P>Эвент можно проверять как значение. А можно и моком. Значением дешевле.
Что значит "дешевле"? И то, и то выполняется микросекунды и никакой сети-диска не требует.
P>>>Проблем может быть сколько угодно, именно конечный результат говорит что правильно а что нет
P>·>Не знаю как у тебя, но у меня большинство ошибок именно логике в реалзиации методов, когда я тупо ошибаюсь в +1 вместо -1, from/to и т.п.
P>В том то и дело — и никакие моки этого не исправят. Потому и нужна проверка на результат.
Моки позволяют писать очень много быстрых и легковесных тестов и легко отлаживать.
P>>>Здесь мы тестируем построение запроса
P>·>Ассерт-то в чём? Что текст запроса 'select * from users where id=?'? У тебя в тесте именно текст забит или что?
P>Я ж вам показал все что надо — deep equality + структура.
deep equality чего с чем? Что написано в коде теста?
P>>>·>Как проверить, что текст запроса хотя бы синтаксически корректен?
P>>>Для этого нам всё равно придется выполнить его на реальной бд. Расскажите, как вы решите задачу вашими моками.
P>·>Я уже рассказывал. Интеграционным тестом repo+db. Текстов запроса в тестах нет, и быть не должно. Как-то так:
P>Вот видите — вам тоже нужны такие тесты Только вы предпочитаете косвенную проверку.
В чём косвенность?
P>Кстати, как вы собираетесь пагинацию да фильтры тестировать? Подозреваю, сгенерируете несколько тысяч значений, и так для каждой таблицы?
Зачем несколько тысяч? Тысячи нужны для перф-тестов. Для тестирования логики достаточно нескольких значений. Но, впрочем, если тебе очень хочется — сгенерировать тысячу значений — есть такая штука как for-loop, например.
P>>>А здесь как вы моками обойдетесь?
P>·>Тут и не надо. Здесь же интеграция с субд тестируется. Моки (стабы?) будут дальше — в бл, в контроллере, етс.
P>Ну да, раз вы протащили в БЛ репозиторий, то и отрезать надо там же. А вариант сделать БЛ иначе, что бы хватило юнитов, вы отбрасываете заранее с формулировкой "по старинке надёжнее"
Так ведь юниты и "делать не по старинке" — это не самоцель. Целью может быть — писать поменьше прод кода, ловить баги как можно раньше и быстрее, упрощать разработку, отладку, етс.
P>·>У тебя был такой код:
P>·>P>·>const getUser = repository.getUser(id);
P>·>const oldUser = db.execute(getUser);
P>·>
P>·>Этот код выглядит так, что getUser выдаваемый из репы не зависит от db (и как ты заявил обещано в clean) и repo покрывается тестами независимо.
P>Есть такое. Они конечно же должны быть согласованы. Моя идея была в том, что бы тестировать построение запроса, а не мокать всё подряд.
Зачем тебе тестировать построение запроса? С т.з. "ожидаемого результата", за который ты так ратуешь, совершенно пофиг какой запрос построился. Главное, что если его выполнить и распарсить recordset — вернётся ожидаемый результат.
P>·>В моём коде выше с MyRepo("connectionString") — мы тестируем только интеграцию двух частей repo + dbms. И ничего больше в таких тестах поднимать не надо.
P>И у меня примерно так же, только моков не будет.
В этом моём коде выше не было моков.
P>>>Что вас смущает? трансформация результата принимает ResultSet и возвращает вам нужный объект.
P>·>Так ведь надо не абы какой ResultSet, а именно тот, который реально возвращается из "select *". У тебя же эти по настоящему связанные сущности тестируются независимо. Что абсолютно бесполезно.
P>1 раз тестируются в составе интеграционного теста, такого же, как у вас. и много раз — в составе юнит-тестов, где нас интересуют подробности
P>- а что если в колонке пусто
Именно. А по итогу получится, что при вставке бд пустую колонку заменяет на дефолтное значение и при выборке там пусто быть просто не может; и окажется, что это дефолтное значение не очень правильное. Получается, что твои тесты тестируют какую-то хрень, "как написано".
P>И это в любом случае нужно делать, и никакие моки от этого не избавляют. Можно просто отказаться от таких тестов типа "у нас смок тест бд". Тоже вариант, и собственно моки здесь снова никак не помогают
Ещё раз повторю. Это и-тесты репо+бд. А моки избавляют от необходимости иметь бд при тестировании всего остального.
P>>>Расскажите, как вы это моками решите
P>·>Моки (стабы) — это для отпиливания зависимостей в тестах. Репу тестируем медленными и-тестами с более-менее реальной субд, а дальше моки (стабы) для быстрого тестирования всего остального.
P>Вот — вам надо медленными тестами тестировать репу — каждый из её методов, раз вы её присобачили. А у меня репа это фактически билдер запросов, тестируется простыми юнит-тестами.
Толку правда никакого от таких юнит-тестов, т.к. они тестируют "как написано".
P>Зато мне нужно будет несколько медленных тестов самого useCase.
Не несколько, а дофига.
P>>>Это одно и то же, и в этом проблема. Я ж вам говорю, как можно иначе — но вы упорно видите только вариант из 90х-00х
P>·>Ты показал это "иначе", но там всё ещё хуже прибито. Вплоть до побуквенного сравнения текста sql.
P>-
P>Вы так и не рассказали, чем это хуже. Вы предложили конский запрос тестировать косвенно
Тем, что тесты пишутся в первую очередь для людей. Может я просто недостаточно профессионал, но я не умею в уме исполнять sql-запросы и уж точно запросто пропущу мелкие синтаксические ошибки. Вот
user.getName() == "Vasya" — мой мозг понять способен, а вот "select * from users where id=?" — я не понимаю эти все закорючки, тем более с учётом того, что каждая субд любит это немного по-разному. Может надо было "from user where" написать? Или "from [user] where"? А фиг знает... Я понятия не имею какой sql правильный.
P>>>Что бы такого не случалось, в тестах должен быть сценарий, который включает возврат. И так для всех апи которые вы выставляете.
P>·>Не понял, если возврат отключен, то и тест должен тестировать, что возврат отключен.
P>Очень даже понятно — вот некто неглядя вкомитал в конфиг что возвраты отключены. Ваши действия?
Ни разу такой проблемы не припомню. Это как "неглядя"? Конфиг, если что, бизнес/operations правят. Если это не должно отключаться, так я просто конфиг грохну.
P>·>Примерное типичное кол-во сценариев реальной системе большего, чем хоумпейдж масштаба.
P>Из этого никак не следует, что и в тестах будет так же. Нам не нужно сравнивать трассы всех тестов вообще. Вы вы еще предложите трассы нагрузочных тестов сравнивать или трассы за весь спринт
P>Нас интересуют трассы конкретных тестов. Если вы можете такое выделить — акцептанс тесты для вас. Если у вас мешанина, и всё вызывает всё да помногу раз с аудитом вперемешку — акцептанс тесты вам не помогут.
Что за трассы тестов? Мы об аудите говорим. Аудит пишется практически каждым действием, т.е. почти каждым тестом бл. Значит при изменении формата аудита — изменится результат всех таких тестов.
P>·>Только не пытайся доказать, что это у них "не работало", потому что тесты у них неправильные. Уж поверь, у них всё работало как надо (им).
P>После ссылки на федеральный закон вдруг "заработало" как надо.
Именно. И причём тут тесты?
P>>>В данном случае вам нужно писать больше кода в тестах, и они будут хрупкими. Другого вы с моками не получите. Потому и убирают зависимости из БЛ целиком, что бы легче было покрывать тестами
P>·>Но ведь не "убирают", а переносят. Что добавляет и количество прод-кода и число тестов.
P>Тестов БЛ — больше, т.к. теперь можно покрывать дешовыми тестами самые разные кейсы, для того и делается.
P>Интеграционных — столько же, как и раньше, — по количеству АПИ и сценариев.
АПИ и сценарии — это и есть БЛ.
P>>>Ага, сидим да годами ждём результаты одного теста. Вам самому не смешно?
P>·>Думаю ваш хоум-пейдж за час тестируется, в лучшем случае.
P>Полтора часа только сборка идет.
Ээээ. шутишь? А тесты сколько времени занимают?
P>>>А с аннотациями взял да и отрендерил манифест.
P>·>Гы-гы. У тебя же, ты сам говорил, что попало где попало — и в переменных окружения, и в конфигах и где угодно эти "аннотации" висят. Сваггер у вас каждые 5 минут новый, да?
P>Вы снова из 90х? Манифест генерируется по запросу, обычно http. Отсюда ясно, что и переменные, и конфиги, и аннотации будут учтены.
Сваггер — это спека для клиентов. Она статическая и меняться должна в идеале никогда.
P>Еще тенантность будет учитывать — для одного тенанта один апи доступен, для другого — другой.
Жуть.
P>·>Ты любишь сложность на пустом месте, притом в прод-коде, я уже понял. Но это плохо. Фреймворк это совсем другая весовая категория. Для того что ты пишешь достаточно extract method refactoring, а не новая архитектура с фреймворками.
P>Ровно наоборот. Чем проще, тем лучше. Фремворковый код он не то что бы особо сложный — просто разновидности вы будете каждый раз писать по месту. Или же отделяете, покрываете сотней тестов, и используете через аннотации без оглядки "а вдруг не сработает"
Есть такая штука, называется code reuse.