Re[91]: Что такое Dependency Rejection
От: Pauel Беларусь http://blogs.rsdn.org/ikemefula
Дата: 09.03.24 12:55
Оценка:
Здравствуйте, ·, Вы писали:

P>>·>Способ экономии ресурсов.

P>>Имено — за счет покрытия.
·>За счёт разделения слона на части.

А как убедиться, что все части собраные вместе дают нужный результат?

P>>Вы сами себя ограбили когда строили вашу пирамиду тестов в виде трапеции — полуинтеграционные на моках + конформанс, которые к вашей системе имеют слабое отношение.

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

Причин может быть много. Качество не сводится к тестам. Более того — тесты в обеспечении качества это минорная часть.

P>>Юнит-тесты вы не пишете, т.к. "это детали реализации, их не надо писать"

·>Юнит-тесты с моками — это тоже юнит-тесты. Ты до сих пор не въехал что такое моки и как ими надо пользоаться.

Юнит-тесты тестируют те самые детали реализации, которые, по вашему, тестировать не нужно.

P>>e2e "слишком долго"

·>Потому что очень много компонент в системе. Разворачивается на кластере в пол сотни машин. Гонять там проверки каждого поля на null просто невозможно.

Какая связь e2e и "гонять проверки каждого поля на null" ? e2e это про сценарий, а не прогон по всем комбинациям полей/значений.

·>Всё красиво, но медленно. Такой один сценарий отрабатывает e2e секунды, может даже минуты. Пока браузер откроется, пока поля введутся, кнопочки всякие кликаются, установятся сессии по разным каналам, подпишутся очереди и т.п. Сложно иметь много таких тестов.


e2e много и не нужно.

·>Угу. Но они не могут тестировать все сценарии, времени не хватит как минимум.

·>А ещё всякие завязанные на реальное время тесты. Как ты e2e будешь тестировать, что через 30 минут неактивностии юзера происходит логаут? Будешь ждать по пол часа каждый раз?

Как без таких тестов сможете убедиться, что на реальной системе у вас действительно эти 30 минут а не месяцы и годы?

Если этот шаг неважный, его в e2e выносить не нужно. А если это важно, то это проверять надо регулярно:
1 юзера не выбрасывает раньше
2 юзера выбрасывает спустя 30 минут
Для этого не нужны разные e2e — это отдельный долгоиграющий сценарий, где юзер делает чтото время от времени. Такие сценарии включать в билд смысла нет. Зато его можно запускать регулярно на работающей системе.

P>>Это намного лучше, чем их отсутствие.

·>Если это действительно намного лучше, это значит что у вас внизу пирамиды дыры зияют.

Вы так и не объяснили, как добавление e2e добавит дыр внизу пирамиды. У вас что, лимит на количество тестов, добавили e2e — теперь надо сотню юнит-тестов удалить? Не пойму ваш кейс.

P>>Это иллюзия. В сложном приложении интеграция не может быть простой, по определению.

·>Не знаю что ты тут подразумеваешь. Обычно вся интеграция происходит в composition root wiring коде, который составляет доли процента от кодовой базы.
·>У тебя да — кода интеграции дохрена, вот и проблемы с тестированием.

Интеграция начинается с того момента, когда вы из одной своей функции вызвали другую. А вы под интеграционным кодом подразумеваете только composition root. Такого кода действительно мало — процент от силы. То есть основной интеграционный код находится как раз вне composition root.

В типичном приложении технологического кода раз в 10-100 и более больше чем кода БЛ. Например, те самые лайки — здесь, условно, всей БЛ просто подсчет юзеров — десяток утверждений покрывает всё это.
Зато система лайков, которая поглощает трафик от ютуба, будет сложнее 90% процентов всех серверных приложений на рынке.

P>>Как это проверяется стартом приложения — не ясно.

·>Я вроде всё расписал с примерами кода. Перечитай если всё ещё неясно, вопросы задавай чего неясно.

Вы, похоже, под интеграционным кодом называете только тот, что в composition root. Контроллер, роутер, юз-кейс — это всё интеграционный код в чистом виде, эти вещи ничего сами не делают. Задача контроллера — связать конкретный вызов от роутера с юзкейсом, валидацией и де/серилизацией. Роутера — конкретный запрос с контроллером. юз-кейс — связать воедино пайплайн для работы БЛ для конкретного запроса.

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

P>>Здесь важно обнаружить проблемы раньше юзеров на проде.
·>Через несколько секунд после релиза юзеры на проде уже могут иметь проблемы. Ещё раз. Мы не можем себе повзолить трогать чего-либо в проде до прогона всех тестов.

Непонятно — вы добавили ровно один тест, и юзеры получили проблему. Такое может быть только в следующих случаях, если
1. тест сам создаёт проблему
2. тест делает проблему видимой

Что у вас за система, когда вам нельзя ни п1 ни п2 ?

P>>Очевидно, что если собирать сведения не раз. а несколько часов, то сведений будет больше, и диагностика будет точнее

·>И эти несколько часов у юзеров будут проблемы на проде.

Откуда возьмутся проблемы у юзеров на проде? Ваши имеющиеся тесты отработали — за счет чего регрессия произойдет?
Новый тест повалит всё? Или сделает проблему видимой?

P>>Затем, что бы

P>>1 обнаружить на самых ранних этапах
·>"после деплоймента" — это для нас очень поздний этап. Сразу после деплоя мы идём домой, какой к хрену круглые сутки?

Ну и идите. У вас варианты
1 о новых проблемах узнать наутро
2 через неделю от суппорта.

Вам какой вариант ближе? Дайте угадаю "у нас до прода баги недоходят"

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

·>Мы собираем сведения ещё до планирования деплоя.
Это все так делают. И никто, кроме лично вас не утверждает "на проде багов быть не может"
Могут.

P>>1 репортают проблемы

·>Это уже полный провал. Будут разборки.

Т.е. проблема стала видимой, и у вас разборки?
По вашему, лучше проблему юзер через суппорт сообщит, да?

P>>Сценарии и так покрыты дешовыми тестами. Эти дешовые тесты, особенно на моках, не покрывают всю интеграцию, что очевидно.

·>Все условно полторы строчки интеграции? Какой ужас!

Вы из своих функций вызываете другие свои функции? Вот вам и интеграционный код. Посмотрите, сколько у вас такого.
Ориентировочно — 99%, если у вас чтото большее hello world

P>>Вот это ваш самый частый аргумент вместе с "словоблудие" итд

·>Конечно, ибо есть более надёжные и адектватные подходы. Надеяться что юзеры репортают проблемы — это уже дно какое-то.

Надеяться, что на проде багов нет — вот это дно. Все адекватные вендоры оговаривают обеспечение гарантий, например,
— баги с прода фиксим в течение x недель, иначе платим пенальти
По такой схеме, работают, например, вендоры ПО для американского медицинского страхования.

P>>·>Где конкретно эта часть есть у тебя? Ты заявлял, что в твоих pattern-тестах ты репозиторий + бд не тестируешь; а в e2e ты каждую переменную на null тестирвать не будешь, да и не сможешь.

P>>Там же где и у вас — это часть интеграционных тестов.
·>Т.е. у тебя есть ровно те же "репозиторий + бд" тесты?

Есть конечно же. Только таких тестов немного — они дают слабые гарантии в силу своей косвенности.

P>>·>Ты тоже.

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

Вероятно, у вас не было такой задачи. Вы видите билдер запросов как часть репозитория или орм который даден вам в готовом виде.
А я вам привел пример задачи, которая требует написание такого билдера.

P>>Именно это нефункциональные требования и описывают — когда именно произойдет событие "юзер создался до конца". Вот здесь как правило запас по времени существенный.

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

Функциональное — это действия юзера согласно требованиям от BA. Всё остальное — нефункциональное.

P>>·>Ну вот это "добро" тебе и надо будет выразить в тестах.

P>>Разумеется.
·>Т.е. без добра существующие тесты должны падатьн. ЧТД.

Все функциональные требования удовлетворены. Непонятно, почему тесты должны падать. У вас, похоже, в функциональных требованиях и имена классов, методов, и даже переменных прописаны.

P>>Расскажите, как лучше. Желательно на тех самых фильтрах — откуда возьмете первый вариант запроса к бд, орм, нужное-вписать, и как будете его тестировать

·>Напишу. Тестировать — тестами.

Для простого запроса это сработает. А если запрос к бд потянет на страницу кода — проще и быстрее проверить это напрямую в бд. За полчаса можно перебрать десяток другой вариантов, и выбрать тот, что проще всего.

P>>репозиторий + бд — если все запросы генерируются самим репозиторием

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

Те самые фильтры. Вы не знаете комбинаций которые приводят к загрузке слишком большого объема данных в память приложения.

P>>Что вы будете ассертить в поведении?

·>Что возвращается ровно столько записей сколько требуется.

Проблемных комбинаций вам никто не сказал. Вы о них узнаете после деплоя на прод.
А до того что вы ассертить будете, что бы исключить такую проблему на проде?

P>>И давно у вас пост-условие стало тестом ?

·>С тех пор как ты так написал. Ты тут предложил проблему "потенциальная загрузка всего содержимого таблицы" исключать тестом с паттерном. Какое ещё постусловие?

Например, .top(10) дает нам пост-условие 'не более 10 записей'
А дальше нам нужно
1. зафиксировать паттерн — проверяем посимвольно за отсутствием других инструментов
2. покрыть это тестом

P>>Пришел некто резкий и дерзкий и фиксанул "тут всё просто, я быстро". На его тестовой бд записей 60 штук, мелочовочка. А на проде это десятки миллионов.

·>Твои тесты с паттернами дерзкого не остановят. Свечка и молитва — гораздо надёжнее твоих тестов! Рекомендую.

Я ж вам говорю — тесты в задаче с построением запроса это вещь второстепенная, в порядке убывания важности
1. дизайн который адресно решает проблему, например, пост-условия — часть этого дизайна.
2. паттерн который соответствует дизайну
3. код ревью
4. тесты, которые фиксируют сохранность 1 и 2

Вы из 1..4 видите только тесты, а всё остальное для вас пустое место.

P>>Полтаблицы это 20 записей? Не понял, что за кейс такой.

·>У тебя же таблица это "это десятки миллионов".

Каким образом .top(10) пропустит десятки миллионов записей? подробнее.

P>>Что значит не появится? Если это пост-условие,оно будет применено для всего выхлопа, а не для разных подветок в вычислениях.

·>Если это постусловие, то и такой тест и не нужен — он не может дать ничего полезного.

Наоборот. Потерять пост-условие в коде всегда легче легкого.
Для того и нужны код ревью и тесты, что бы сохранить свойства кода

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

·>Это значит, что и тесты могут пропустить некий вход, для которого твой билдер пропустит твой top(10) на выходе.

Цитирую себя

Я ж вам говорю — тесты в задаче с построением запроса это вещь второстепенная, в порядке убывания важности
1. дизайн который адресно решает проблему, например, пост-условия — часть этого дизайна.
2. паттерн который соответствует дизайну
3. код ревью
4. тесты, которые фиксируют сохранность 1 и 2

Вы из 1..4 видите только тесты, а всё остальное для вас пустое место.


P>>Умеют — здесь только код-ревью может помочь. О чем я вам и говорю в который раз.

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

Облегчают. Вместо погружения во все детали реализации во время код-ревью, вы смотрите какие тесты были исключены.

·>Я рассказал о false positive/negative таких тестов в ответе Sinclair. Да и ты сам написал только что — нужен кто-то очень умный для проведения PR для гарантии сохранности пост-условия, а всё потому, что твои тесты ничего не дают.


Вы пока и такого не выдали — за отсутствием нужных комбинаций ваше решение вываливает проблему на юзера на проде.

·>Как инструмент упрощения и ускорения процесса разработки и контроля качества. Чтобы вручную не возиться, чтобы в прод не лазить за каждым чихом, чтобы за минуты проводить acceptance, а не ждать часами после релиза репортов от юзеров.


acceptance в минутах считается только для hello world.

P>>каким образом в одной таблице users может быть несколько колонок id ? byId — это запрос к праймари кей. Ломается только если схема поломана.

·>externalId, internalId, globalId, replicationId и хз что. byId — ну может быть и праймари, очень частный скучный случай. А более весёлым будет если у тебя появится byId(List<int> ids) и будешь возвращать всю базу для пустого входного списка, для которого забудешь e2e тест написать.

Непонятно, что за кунсткамера — запрос byId а на пустой список аргументов вгружается вся база?

·>Я имел в виду, что если реквест потенциально выдаёт слишком много записей, то тупо брать только первые 10 — это очень странное решение. По уму надо либо делать пейджинг


Вот вы сами себе ответили. Только проверять пейджинг нужно не только косвенно, как вы это делаете, а напрямую — контролируя запрос.

P>>Ловко вы опровергли теорему Райса!

P>>В т.Райса нетривиальное свойство означает, что есть функции, которые им обладают, а есть те, которые не обладают.
·>Не совсем. Тривиальным условием например будет "код анализируемой программы содержит инструкцию X".

Теорема Райса не про код, а про семантику. Содержит инструкцию X — такое успешно решает любой компилятор.
А вот "выполнится ли инструкция X" — вот такое сводится к проблеме останова.

Современная разработка это проверка свойств программы через свойства код — та самая статическая типизация, контракты — инварианты, пред-, пост- условия, структурная эквивалентность и тд и тд

Вы же своими косвенными тестами воюете именно с теоремой Райса. Забавно, да?

·>Нет. Опровержением будет, если я напишу алшгоритм который будет для любого данного f утверждать, что этот самый f всегда возвращает true (что по твоим заверениям твои тесты тестируют "мой тест гарантирует, что top(10) будет стоять для всех комбинаций фильтров"). А для () -> true — данный конкретный случай, код известен полностью заранее и для него существует известный полный комплект входов/выходов.


Вот-вот. Вы путаете синтаксис и семантику. Любой компилятор справляется и с синтаксисом, и со структурой, но не с семантикой.

P>>Ручное тестирование плохо не само по себе, а когда оно увеличивает время разработки

P>>А когда сокращает — очень даже хорошо
·>Это сигнал, что автоматическое тестирование организовано плохо.

Расскажите, как вы будете искать новые, неизвестные ранее проблемы, используя ваши автоматическими тесты
Например — те самые фильтры. Вы так и не показали ни одного автоматического теста, который находит проблему.

У вас в очередной раз какая то детская идеализация инструмента. Любой инструмент, если он реален, имеет недостатки.
А раз так — то имеет границы применимости.
Там, где у автоматических тестов начинаются недостатки, у других методов будут преимущества.

P>>Я снова выделил, что бы вам виднее было. Посимвольно это потому, что у меня в конкретном случае нет ни json, ни orm, ни еще какого ast.

·>Какие другие случаи бывают и как будет выглядеть код?

Я вам уже приводил пример
const request = create(
    User, 
    u => u.many(q => q.filter(f => f(filterExpression))
                      .orderBy(o => u('created'))
                  .top(10)
    )
);


Развитией этой идеи вам Синклер показал. Но похоже, вы учитесь только на своих ошибках

P>>И так будете для каждой комбинации, да? Дадите шанс юзеру убиться об ваш билдер фильтров?

·>Зачем для каждой? Классифицируем, моделируем, алгоритмизируем, етс. Вон как у Sinclair оказалось, что функция-то линейная и никаких экспоненциальых переборов комбинаций и не требуется, покрывается полностью от силы десятком тестов.

Ну так вы от его подхода отказались. А раз так — то вам нужны тесты на тех самых комбинациях которых у вас нет

P>>·>Ок, допустим ногами. А какие в базе будут данные на которой этот чаи-гпт будет проверять свои запросы вножную? За 2025 год, правильно?

P>>Жалко, что нет шрифта больше чем h1
·>Ты не ответил на вопрос "А какие в базе будут данные в момент ручной проверки"?

Ищите по огромным синим буквам.

P>>·>Потом система меняется и паттерн перестаёт функционировать проверенным вручную способом, а тестам — пофиг, они вечнозелёные.

P>>А интеграционные тесты вы забыли, да?
·>Ты заявлял, что интеграционными тестами ты не проверяешь всё.

Комбинации — нет, не проверяю. Т.к. интеграционные тесты здесь ничего не добавляют. Сколько бы вы комбинаций не проверили интеграционными тестами, это всё равно проверка интеграции. А вот юнитами можно оформить куда лучше.

P>>В том то и дело — количество e2e никак не зависит от количества комбинаций в фильтрах.

·>Почему?

Потому, что e2e это про интеграцию верхнего уровня.
Задача проверки "фильтры работают согласно спеке" дробится на части, и проверяется на всех уровнях
1 e2e — "юзер может отфильтровать"
2 интеграционные, функциональные — "фильтры умеют ... список фич прилагается"
4 юнит-тесты, компонентные — "выражение фильтра генерируется согласно дизайну для каждого паттерна в приложении, например (запрос, предикат, лимит) (запрос, предикат, вложеный запрос, предикат, лимит ) итд итд"

P>>2 находится ли оно на своем месте

P>>3 все ли ветки ифов получат его
·>Именно. И для этого нужно знать комбинации которые будут проблемными.

Вам не нужен перечень всех четных или нечетнных чисел, что бы написать фунции isOdd и isEven.
Вам нужно знание о том, как устроены четные и нечетные — у одних младший бит 1, у других — младший бит 0

В переводе на фильтры — у запроса всегда есть лимит.
Отредактировано 09.03.2024 14:17 Pauel . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.