Здравствуйте, Pauel, Вы писали:
P>>>Имено — за счет покрытия.
P>·>За счёт разделения слона на части.
P>А как убедиться, что все части собраные вместе дают нужный результат?
smoke tests — как вишенка на торт, но в остальном — проверять две вещи: части работают, части собираются между собой нужным образом.
P>>>Вы сами себя ограбили когда строили вашу пирамиду тестов в виде трапеции — полуинтеграционные на моках + конформанс, которые к вашей системе имеют слабое отношение.
P>·>В чём ограбили? На прогон тестов и релиз мы тратим как лохи всего пол часа и уныло идём домой, скучаем на прод-саппорте, когда за углом тесты гоняют неделями и нон-стоп вечеринка в проде с юзерами?
P>Причин может быть много. Качество не сводится к тестам. Более того — тесты в обеспечении качества это минорная часть.
Минорная?! Без тестов обеспечить качество практически невозможно.
P>>>Юнит-тесты вы не пишете, т.к. "это детали реализации, их не надо писать"
P>·>Юнит-тесты с моками — это тоже юнит-тесты. Ты до сих пор не въехал что такое моки и как ими надо пользоаться.
P>Юнит-тесты тестируют те самые детали реализации, которые, по вашему, тестировать не нужно.
Это ты опять нафантазировал. Те детали реализации которые _ты_ предлагаешь тестировать — верно, не нужно.
P>>>e2e "слишком долго"
P>·>Потому что очень много компонент в системе. Разворачивается на кластере в пол сотни машин. Гонять там проверки каждого поля на null просто невозможно.
P>Какая связь e2e и "гонять проверки каждого поля на null" ? e2e это про сценарий, а не прогон по всем комбинациям полей/значений.
Связь в том, что ты в своих юнит-тестах предлагаешь проверять поля на null, но интеграцию этого же кода с реальной субд — провеять уже через e2e.
P>·>Всё красиво, но медленно. Такой один сценарий отрабатывает e2e секунды, может даже минуты. Пока браузер откроется, пока поля введутся, кнопочки всякие кликаются, установятся сессии по разным каналам, подпишутся очереди и т.п. Сложно иметь много таких тестов.
P>e2e много и не нужно.
Где ты предлагаешь проверять поля на null тогда?
P>·>Угу. Но они не могут тестировать все сценарии, времени не хватит как минимум.
P>·>А ещё всякие завязанные на реальное время тесты. Как ты e2e будешь тестировать, что через 30 минут неактивностии юзера происходит логаут? Будешь ждать по пол часа каждый раз?
P>Как без таких тестов сможете убедиться, что на реальной системе у вас действительно эти 30 минут а не месяцы и годы?
Ты и со своими тестами не сможешь убедиться. Ещё раз, это достигается другим способом. Строишь модель физического времени, например как int64 unix-epoch nanoseconds. А дальше в тестах тупо прибавляешь разное количество наносекунд к моку источника времени и смотришь, что логаут не|произошел. Тогда и конкретное количество времени неважно, хоть 30 минут, хоть 30 лет — тест будет выполяться мгновенно в любом случае. И нет никаких проблем прогонять этот тест каждый раз перед каждым релизом.
P>Если этот шаг неважный, его в e2e выносить не нужно. А если это важно, то это проверять надо регулярно:
Ок... ну может логаут неважный. Но вот например "отослать statement в конце каждого месяца" — шаг ооочень важный. Тоже e2e тест сделаешь со sleep(1 month)?
P> Зато его можно запускать регулярно на работающей системе.
P>>>Это намного лучше, чем их отсутствие.
P>·>Если это действительно намного лучше, это значит что у вас внизу пирамиды дыры зияют.
P>Вы так и не объяснили, как добавление e2e добавит дыр внизу пирамиды.
А зачем я буду объяснять бред который ты нафантазировал?
P> У вас что, лимит на количество тестов, добавили e2e — теперь надо сотню юнит-тестов удалить? Не пойму ваш кейс.
Падение e2e-теста означает дыру в нижележащих тестах. Если ваши e2e-тесты падают, чаще чем рак на горе свистит, значит у вас внизу пирамиды дырявые тесты.
P>·>Не знаю что ты тут подразумеваешь. Обычно вся интеграция происходит в composition root wiring коде, который составляет доли процента от кодовой базы.
P>·>У тебя да — кода интеграции дохрена, вот и проблемы с тестированием.
P>Интеграция начинается с того момента, когда вы из одной своей функции вызвали другую. А вы под интеграционным кодом подразумеваете только composition root. Такого кода действительно мало — процент от силы. То есть основной интеграционный код находится как раз вне composition root.
Я под интеграционным кодом подразумеваю который вызывает "те ошибки, которые проходят мимо ваших моков". Так вот мимо моков проходят ошибки в доле процента кода, который выполняется только при реальном старте приложения, код в том самом composition root.
P>В типичном приложении технологического кода раз в 10-100 и более больше чем кода БЛ. Например, те самые лайки — здесь, условно, всей БЛ просто подсчет юзеров — десяток утверждений покрывает всё это.
P>Зато система лайков, которая поглощает трафик от ютуба, будет сложнее 90% процентов всех серверных приложений на рынке.
У технологического кода другой бизнес-домен. Технологический код, который, скажем, реплицирует базу записей в несколько датацентров — оперирует не бизнес-сущностью лайка, а "запись данных", "датацентр" и т.п. А дальше уже идёт интеграция, когда мы в качестве записи берём лайк и получаем надёжное реплицированное хранилище лайков. У тебя же твой билдер билдит конкретный запрос сущности из бизнес-домена, но вместо ассертов бизнес-сущностей ты тестируешь технические детали.
P>>>Как это проверяется стартом приложения — не ясно.
P>·>Я вроде всё расписал с примерами кода. Перечитай если всё ещё неясно, вопросы задавай чего неясно.
P>Вы, похоже, под интеграционным кодом называете только тот, что в composition root. Контроллер, роутер, юз-кейс — это всё интеграционный код в чистом виде, эти вещи ничего сами не делают. Задача контроллера — связать конкретный вызов от роутера с юзкейсом, валидацией и де/серилизацией. Роутера — конкретный запрос с контроллером. юз-кейс — связать воедино пайплайн для работы БЛ для конкретного запроса.
Только тот код, что в composition root сложно покрывается тестами, т.к. там создаются и инжектятся реальные объекты и требует дорогого e2e тестирования. Остальное — неинтересно, ибо быстро, тривиально и надёжно тестируется с помощью моков.
Если вы не в сосотянии протестировать контроллер, роутер, юзекйс валидацию и сериализацию без e2e — у вас проблемы.
P>>>Вы и запускаете его для релиза — только результаты собираете не разово, а в течение первых часов после деплоя.
P>>>Здесь важно обнаружить проблемы раньше юзеров на проде.
P>·>Через несколько секунд после релиза юзеры на проде уже могут иметь проблемы. Ещё раз. Мы не можем себе повзолить трогать чего-либо в проде до прогона всех тестов.
P>Непонятно — вы добавили ровно один тест, и юзеры получили проблему. Такое может быть только в следующих случаях, если
Ты предложил прогонять не все тесты перед выкаткой в прод. Т.е. вы добавили тест, но он не отработал, т.к. random не выпал. Отработает "в течение первых часов после деплоя". А у юзеров проблемы уже прямо сейчас.
P>1. тест сам создаёт проблему
P>2. тест делает проблему видимой
P>Что у вас за система, когда вам нельзя ни п1 ни п2 ?
Бред какой-то. Откуда ты это вообще взял?
P>>>Очевидно, что если собирать сведения не раз. а несколько часов, то сведений будет больше, и диагностика будет точнее
P>·>И эти несколько часов у юзеров будут проблемы на проде.
P>Откуда возьмутся проблемы у юзеров на проде?
Не все тесты выполнены.
P>Ваши имеющиеся тесты отработали — за счет чего регрессия произойдет?
Наши да, ваши — не все.
P>>>Затем, что бы
P>>>1 обнаружить на самых ранних этапах
P>·>"после деплоймента" — это для нас очень поздний этап. Сразу после деплоя мы идём домой, какой к хрену круглые сутки?
P>Ну и идите. У вас варианты
P>1 о новых проблемах узнать наутро
P>2 через неделю от суппорта.
P>Вам какой вариант ближе? Дайте угадаю "у нас до прода баги недоходят"
Те, для которых написаны тесты — ясен пень не доходят. Если какой-либо тест красный для данной версии системы — в прод она не попадёт.
P>>>2 сразу собрать максимальное количество сведений. По этим тестам у вас больше всего сведений, как и что воспроизвестир
P>·>Мы собираем сведения ещё до планирования деплоя.
P>Это все так делают.
Откуда тогда у тебя берутся красные тесты после деплоя в прод?
P>И никто, кроме лично вас не утверждает "на проде багов быть не может"
Ты меня заебал. Не пиши свои бредовые фантазии как мои цитаты. Я такого никогда нигде не утверждал.
P>>>1 репортают проблемы
P>·>Это уже полный провал. Будут разборки.
P>Т.е. проблема стала видимой, и у вас разборки?
P>По вашему, лучше проблему юзер через суппорт сообщит, да?
Ты опять контектст потерял? Твоё высказывание было "[Юзеры] репортают проблемы". У нас это значит разборки. Если у вас не так, могу только посочувствовать вашим юзерам.
P>>>Сценарии и так покрыты дешовыми тестами. Эти дешовые тесты, особенно на моках, не покрывают всю интеграцию, что очевидно.
P>·>Все условно полторы строчки интеграции? Какой ужас!
P>Вы из своих функций вызываете другие свои функции? Вот вам и интеграционный код. Посмотрите, сколько у вас такого.
Непокрытого лёгкими тестами? Полторы строчки, всё верно.
P>Ориентировочно — 99%, если у вас чтото большее hello world
Ну если у вас 99%, сочувствую.
P>>>Вот это ваш самый частый аргумент вместе с "словоблудие" итд
P>·>Конечно, ибо есть более надёжные и адектватные подходы. Надеяться что юзеры репортают проблемы — это уже дно какое-то.
P>Надеяться, что на проде багов нет — вот это дно. Все адекватные вендоры оговаривают обеспечение гарантий, например,
Ну так бросай ружьё, да всплывай!
P>>>Там же где и у вас — это часть интеграционных тестов.
P>·>Т.е. у тебя есть ровно те же "репозиторий + бд" тесты?
P>Есть конечно же. Только таких тестов немного — они дают слабые гарантии в силу своей косвенности.
Слабые относительно чего?
P>>>Именно. И я этим знанием пользуюсь, а вы — нет. например, я точно знаю, как выглядит проблема с фильтрами — вгружается вообще всё.
P>·>А я не знаю такой проблемы... не было у нас такой. ЧЯДНТ?
P>Вероятно, у вас не было такой задачи. Вы видите билдер запросов как часть репозитория или орм который даден вам в готовом виде.
P>А я вам привел пример задачи, которая требует написание такого билдера.
Возможно. Непонятно почему этот билдер специфичный под этот конкретный метод, а не универсальный типа linq со своими тестами.
P>·>"Событие" — это функциональная сущность. Возврат результата из метода — это событие. Вначале у тебя событие означало "юзер создался до конца", а после того как ты переложил обработку в очередь, то смысл события поменялся на "создался, но не очеь". Это функциональное изменение.
P>Функциональное — это действия юзера согласно требованиям от BA. Всё остальное — нефункциональное.
Если юзер не может выполнить функцию, то это отказ системы, нарушение требований, то что отказ временный — не так важно.
P>·>Т.е. без добра существующие тесты должны падатьн. ЧТД.
P>Все функциональные требования удовлетворены. Непонятно, почему тесты должны падать.
Потому что попытка юзера выполнить функцию проваливается из-за ошибок в синхронизации.
P>>>Расскажите, как лучше. Желательно на тех самых фильтрах — откуда возьмете первый вариант запроса к бд, орм, нужное-вписать, и как будете его тестировать
P>·>Напишу. Тестировать — тестами.
P>Для простого запроса это сработает. А если запрос к бд потянет на страницу кода —
Это значит у вас примитивная модель тестов, не вытягивает страницу кода.
P>проще и быстрее проверить это напрямую в бд. За полчаса можно перебрать десяток другой вариантов, и выбрать тот, что проще всего.
Понимаю, все там были в студенчестве.
P>>>Что вы будете ассертить в поведении?
P>·>Что возвращается ровно столько записей сколько требуется.
P>Проблемных комбинаций вам никто не сказал.
Ну если вам сказали, поздравляю, но не всем так везёт...
P>А до того что вы ассертить будете, что бы исключить такую проблему на проде?
Ещё раз, эта проблема ассертами в тестах не решается.
P>>>И давно у вас пост-условие стало тестом ?
P>·>С тех пор как ты так написал. Ты тут предложил проблему "потенциальная загрузка всего содержимого таблицы" исключать тестом с паттерном. Какое ещё постусловие?
P>Например, .top(10) дает нам пост-условие 'не более 10 записей'
P>А дальше нам нужно
P>1. зафиксировать паттерн — проверяем посимвольно за отсутствием других инструментов
P>2. покрыть это тестом
Верно. Но ты не догоняешь, что во 2м пункте можно просто ассертить число записей, с тем же успехом, но лучшим результатом.
P>Я ж вам говорю — тесты в задаче с построением запроса это вещь второстепенная, в порядке убывания важности
P>1. дизайн который адресно решает проблему, например, пост-условия — часть этого дизайна.
P>2. паттерн который соответствует дизайну
P>3. код ревью
P>4. тесты, которые фиксируют сохранность 1 и 2
P>Вы из 1..4 видите только тесты, а всё остальное для вас пустое место.
Не потому что пустое место, а потому что мы тут обсуждаем тесты и разные подходы к тестированию, а 1..3 — оффтопик. Но ты всё не успокоишься и продолжаешь словоблудить.
P>>>Полтаблицы это 20 записей? Не понял, что за кейс такой.
P>·>У тебя же таблица это "это десятки миллионов".
P>Каким образом .top(10) пропустит десятки миллионов записей? подробнее.
Поставлен не туда или поставлен условно.
P>>>Что значит не появится? Если это пост-условие,оно будет применено для всего выхлопа, а не для разных подветок в вычислениях.
P>·>Если это постусловие, то и такой тест и не нужен — он не может дать ничего полезного.
P>Наоборот. Потерять пост-условие в коде всегда легче легкого.
P>Для того и нужны код ревью и тесты, что бы сохранить свойства кода
_такой_ тест не нужен. Нужен другой тест.
P>>>Поскольку билдер сложный, то можно сказать, что да, магически — прямой связи входа и выхода вы на глазок не обнаружите. Потому и надо подстраховываться в тестах.
P>·>Это значит, что и тесты могут пропустить некий вход, для которого твой билдер пропустит твой top(10) на выходе.
P>Цитирую себя
P>Вы из 1..4 видите только тесты, а всё остальное для вас пустое место.
Т.е. твои тесты — проверяют меньше, чем мои. Мои, помимо того, что кол-во записей ограничивается 10ю так же протестирует и валидность запросов, подстановку парамов и т.п.
P>>>Умеют — здесь только код-ревью может помочь. О чем я вам и говорю в который раз.
P>·>Так я об этом и говорю, что тесты в твоём виде бесполезны и даже вредны, т.к. код-ревью усложняют.
P>Облегчают. Вместо погружения во все детали реализации во время код-ревью, вы смотрите какие тесты были исключены.
Ты о чём? Что за исключённые тесты?
P>·>Я рассказал о false positive/negative таких тестов в ответе Sinclair. Да и ты сам написал только что — нужен кто-то очень умный для проведения PR для гарантии сохранности пост-условия, а всё потому, что твои тесты ничего не дают.
P>Вы пока и такого не выдали — за отсутствием нужных комбинаций ваше решение вываливает проблему на юзера на проде.
Ваше вываливает больше проблем на проде.
P>·>Как инструмент упрощения и ускорения процесса разработки и контроля качества. Чтобы вручную не возиться, чтобы в прод не лазить за каждым чихом, чтобы за минуты проводить acceptance, а не ждать часами после релиза репортов от юзеров.
P>acceptance в минутах считается только для hello world.
Завидуешь, да? Ну вообще говоря acceptance гоняется на кластере, вот и укладывается в пол часа.
P>>>каким образом в одной таблице users может быть несколько колонок id ? byId — это запрос к праймари кей. Ломается только если схема поломана.
P>·>externalId, internalId, globalId, replicationId и хз что. byId — ну может быть и праймари, очень частный скучный случай. А более весёлым будет если у тебя появится byId(List<int> ids) и будешь возвращать всю базу для пустого входного списка, для которого забудешь e2e тест написать.
P>Непонятно, что за кунсткамера — запрос byId а на пустой список аргументов вгружается вся база?
Ну можно придумать что, например, для пустого списка cгенерится пустой фильтр.
P>·>Я имел в виду, что если реквест потенциально выдаёт слишком много записей, то тупо брать только первые 10 — это очень странное решение. По уму надо либо делать пейджинг
P>Вот вы сами себе ответили. Только проверять пейджинг нужно не только косвенно, как вы это делаете, а напрямую — контролируя запрос.
Не нужно.
P>>>Ловко вы опровергли теорему Райса!
P>>>В т.Райса нетривиальное свойство означает, что есть функции, которые им обладают, а есть те, которые не обладают.
P>·>Не совсем. Тривиальным условием например будет "код анализируемой программы содержит инструкцию X".
P>Теорема Райса не про код, а про семантику. Содержит инструкцию X — такое успешно решает любой компилятор.
P>А вот "выполнится ли инструкция X" — вот такое сводится к проблеме останова.
В тесте же конкретный сценарий — запускаем код, дождались завершения, проверяем, выполнилась ли инструкция X. Никакой проблемы останова.
P>·>Нет. Опровержением будет, если я напишу алшгоритм который будет для любого данного f утверждать, что этот самый f всегда возвращает true (что по твоим заверениям твои тесты тестируют "мой тест гарантирует, что top(10) будет стоять для всех комбинаций фильтров"). А для () -> true — данный конкретный случай, код известен полностью заранее и для него существует известный полный комплект входов/выходов.
P>Вот-вот. Вы путаете синтаксис и семантику. Любой компилятор справляется и с синтаксисом, и со структурой, но не с семантикой.
Так ты зачем-то синтаксис и проверяешь "есть ли в коде .top(10)".
P>·>Это сигнал, что автоматическое тестирование организовано плохо.
P>Расскажите, как вы будете искать новые, неизвестные ранее проблемы, используя ваши автоматическими тесты
Если есть зрелая система автоматических тестов, это значит, что можно делая exploratory testing (это видимо что ты тут имеешь в виду) описывать новые сценарии, пользуясь уже готовым test harness. Тут же запускать их, смотреть на поведение системы и, может быть, даже закоммитить результат как новый автотест.
P>Например — те самые фильтры. Вы так и не показали ни одного автоматического теста, который находит проблему.
Если я правильно понял проблему, то она не находится никакими тестами, хоть ручными, хоть ножными. Если только случайно.
P>У вас в очередной раз какая то детская идеализация инструмента. Любой инструмент, если он реален, имеет недостатки.
P>А раз так — то имеет границы применимости.
P>Там, где у автоматических тестов начинаются недостатки, у других методов будут преимущества.
Если ты под другими методами имеешь в виду ручные тесты, то у меня для тебя плохие новости.
P>>>Я снова выделил, что бы вам виднее было. Посимвольно это потому, что у меня в конкретном случае нет ни json, ни orm, ни еще какого ast.
P>·>Какие другие случаи бывают и как будет выглядеть код?
P>Я вам уже приводил пример
[код скипнут]
Главное пока умалчиваешь: тест как выглядит для этого кода? Ну где там deep.eq, pattern или что?
P>Развитией этой идеи вам Синклер показал. Но похоже, вы учитесь только на своих ошибках
Это вряд ли. Но вы даже на своих не учитесь.
P>·>Зачем для каждой? Классифицируем, моделируем, алгоритмизируем, етс. Вон как у Sinclair оказалось, что функция-то линейная и никаких экспоненциальых переборов комбинаций и не требуется, покрывается полностью от силы десятком тестов.
P>Ну так вы от его подхода отказались. А раз так — то вам нужны тесты на тех самых комбинациях которых у вас нет
Это твои бредни.
P>>>Жалко, что нет шрифта больше чем h1
P>·>Ты не ответил на вопрос "А какие в базе будут данные в момент ручной проверки"?
P>Ищите по огромным синим буквам.
Там нет ответа на этот вопрос. Там сказано "
далее ...бд минимально заполненой", а что было с бд до "далее" — ты надёжно скрываешь.
P>>>А интеграционные тесты вы забыли, да?
P>·>Ты заявлял, что интеграционными тестами ты не проверяешь всё.
P>Комбинации — нет, не проверяю. Т.к. интеграционные тесты здесь ничего не добавляют. Сколько бы вы комбинаций не проверили интеграционными тестами, это всё равно проверка интеграции. А вот юнитами можно оформить куда лучше.
Какие поля когда могут быть null — это дохрена комбинаций в типичном приложении. Т.е. получается, что ты интеграцию null-полей проверяешь только на юзерах.
P>>>В том то и дело — количество e2e никак не зависит от количества комбинаций в фильтрах.
P>·>Почему?
P>Потому, что e2e это про интеграцию верхнего уровня.
P>Задача проверки "фильтры работают согласно спеке" дробится на части, и проверяется на всех уровнях
P>1 e2e — "юзер может отфильтровать"
P>2 интеграционные, функциональные — "фильтры умеют ... список фич прилагается"
P>4 юнит-тесты, компонентные — "выражение фильтра генерируется согласно дизайну для каждого паттерна в приложении, например (запрос, предикат, лимит) (запрос, предикат, вложеный запрос, предикат, лимит ) итд итд"
Где у тебя null-поля с базой тестируются?
P>·>Именно. И для этого нужно знать комбинации которые будут проблемными.
P>Вам не нужен перечень всех четных или нечетнных чисел, что бы написать фунции isOdd и isEven.
P>Вам нужно знание о том, как устроены четные и нечетные — у одних младший бит 1, у других — младший бит 0
Отлично. Вот ты и выявил какие комбинации будут проблемными. Придумать входные парамы и ожидания с вариациями младшего бита. Т.е. и тестировать надо именно на комбинациях младшего бита.
P>В переводе на фильтры — у запроса всегда есть лимит.
"у запроса всегда есть лимит" в переводе в твоём случае будет "функция isOdd всегда использует битовую операцию взятия младшего бита".