Re[86]: Что такое Dependency Rejection
От: · Великобритания  
Дата: 04.03.24 12:26
Оценка:
Здравствуйте, Pauel, Вы писали:

P>·>Я здесь писал про интеграционные тесты вида "когда у нас дошла очередь интеграционного тестирования, где (медленно!) тащатся реальные данные из реальной базы, выполняется (медленный!) UI workflow с навигацией от логина до этого отчёта, и т.п.". И 1% тут получается не потому что нам лень покрывать всё на 100%, а потому что эти тесты медленные, т.е. 100% будет требовать в 100 раз больше времени, чем 1%. А с учётом праздников и выходных это означает, что вместо получаса это выльется в неделю.

P>Будут. Но их всё равно нужно делать, т.к. никакие другие виды тестов их не заменяют, только кое где могут скомпенсировать.
Нужно, но как-то всё равно придётся покрывать остальные 99%. Вот тут и приходят на помощь моки и конф-тесты.

P>Например — если логика сложная, а воркфлоу простой, то можно забить на e2e и заложиться на юнит-тесты.

P>Если у вас сложный воркфлоу, то его нечем протестировать, кроме вот этих e2e
Я тебе уже рассказал чем: моки и конф-тесты.

P>>>Оставшиеся 99% ваши моки не заткнут, как бы вы не старались. Хоть обпишитесь.

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

P>И вы проверяете, что ваш код справляется с этой отсебятиной.

P>Как он будет справляться с интеграцией — остаётся неизвестным, вообще.
Я тебе уже объяснил как это сделать известным.

P>Ответ даёт исключительно прямая проверка этой интеграции

Нет, не исключительно.

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

P>·>В хоумпейдж приложении — запросто. В реальном приложениии — всё грустно. Вот взять какой-нибудь FX Options. Есть несколько видов Options: Vanilla, Barrier, Binary, KnockIn/Out, Accumulator, Swap, Buttefly, &c. У каждого вида ещё нескольо вариаций Call/Put, Buy/Sell, Hi/Lo, &c. Потом разница algo/manual прайсинг, потом дюжина вариантов booking. И эти все комбинации — валидные сценарии. Одних таких тестов для разных комбинаций будут сотни. В e2e это будет часами тестироваться. Поэтому и приходится бить на части, мокать зависимости и тестировать по частям.
P>Итого, с ваших слов "задача сложная, нахер e2e, будем моки писать и сделаем вид, что это равноценная замена"
Не "нахер e2e", а "хрень получается с e2e".

P>e2e у вас должен покрывать все ключевые опции, т.к. это разные сценарии.

P>Еще раз — все.
P>Если вам непонятно — все означает все.
P>На случай если вы и здесь игнорите — все.
P>Вопрос только в том, как вы будете временем распоряжяться.
P>1 Аксиома — количество юнит-тестов и полуинтеграционных на моках никак не влияет на количество e2e. Никак, нисколько!
Тавтология. Я говорю о другом: количество e2e ограничивается доступными ресурсами. Чем меньше доступных e2e, тем больше приходится вкладываться в юнит и "полуинтеграционные".

P> юниты и ваши полуинтеграционные пишите сколько надо, что бы быстро детектить мелочевку во время разработки

P>2 e2e можно сделать так -
P>- при билде — три самых частых
P>- при деплое — три десятка самых частых
P>- после деплоя — всё вместе, рандомно, то одно, то другое, с таким расчетом, что бы за день вы получили сведения что где может отвалиться
Не годится. Мы не можем релизиться только с тремя десятками сценариев. Надо проверять 100%.

P>Итого — ваши 99% кейсов вы будете тестировать после деплоя. Боитесь делать это на проде — делайте на стейдже, деве и тд.

Не можем мы деплоить продукт оттестированный на 1%.

P>Как решить:

P>1. деплоймент, например, стейдж, отдаёт манифест — версия-тег, урл репозитория с тестами конкретной этой версии.
P>2. e2e тесты умеют запуск рандомного сценария
P>3. каждые N минут запускаете `curl <stage>/manifest.test | run-test --random --url <stage>`
Т.е. качество продукта проверяем случайно. Это только для хоумпейджей годится.

P>4. результаты запусков кладете не абы куда, а в репозиторий

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


P>>>Если же вы хотите заткнуть 99% непокрытых юзкейсов мониторингом на основе с healthcheck — чтото здесь не сходится.

P>·>Ты не понял что делает healthcheck.
P>Это ж не я пытаюсь заменить 99% e2e тестов моками.
Тебе повезло, что вам позволительно сыпаться ошибками в проде.

P>>>Вы показали теоретически возможные ошибки при построении запроса "а вдруг не учтем дефолтное значение". Ну так это и у вас будет — если чего то не учтете.

P>·>Если чего-то не учтёте — та же проблема и у вас. Я говорю о другом. Ты вроде учёл "а что если поле null" — и подставил nullзначение в своём pattern-тесте и прописал ожидания, тест зелёный. Но ты не учёл, что реальная субд null заменяет null на дефолтное значение и ведёт не так как ты предположил в своём тесте. А прогонять полные тесты "где (медленно!) тащатся реальные данные" для каждого поля где потенциально может быть null — ты не дождёшься окончания.
P>Это полные тесты, где всё тащится медленно и реально — они нужны и вам, и мне. Если вы не учли этот кейс с нулём, то будет проблема. Ровно так же и у меня.
Ясен пень. Ты вообще читаешь что я пишу? Суть в том, что даже если вы учли кейс с нулём, то у вас всё равно будет проблема, а нас проблемы не будет.

P>·>Режь слона на части, тогда всё запалит.

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

P>И задача с фильтрами это про комбинации

Может я не понял твою задачу правильно, но в моём понимании, фильтры это задача которая сводится к одному компоненту, который может быть протестирован отдельно, а не комбинации различных компонент, которые надо интегрировать и тестировать целиком. Для тестирования фильтров не нужно "выполняется (медленный!) UI workflow с навигацией от логина до этого отчёта", не нужен логин и UI для тестирования комбинации фильтров.

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

P>·>Про 5 минут я не фантазировал, это твоя цитата.
P>Я нигде не говорил, что юзер должен ждать 5 минут. Даже если профиль создаётся неделю, редактировать данные можно сразу.
Говорил, цитирую: чудесные вещи типа "после того, как сделал var u = service.CreateUser(...), нужно выждать не менее 5 минут перед service.UpdateUser(u, ...)".

P>Вам нужно спросить у BA, какие данные надо сейчас, а какие — вообще.

Именно — вопросы за которые отвечает BA — это функциональные требования по сути.

P>·>Да похрен на архитектуру, главное — это означает, что какие-то операции временно перестанут работать как раньше, меняется happens-before гарантии. Появляется необходимость синхронизации, т.е. функциональное изменение, модификация API, документации и т.п. Тесты ну просто обязаны грохнуться!

P>Вы похоже так и не поняли, что такое функциональные требования, а что такое нефункциональные.
P>Пример:
P>Функциональные — админ может создать спейс для юзера, админ может отредактировать профиль юзера.
P>Нефункциональные — профиль юзера можно редактировать сразу после создания. Профиль юзера создаётся за 100 мсек. админ не должен ждать чего то там.
У тебя есть бизнес-функция "создать юзера". Результат который возвращается из этой функции — это тоже функциональный элемент. Результат говорит о том, что что-то произошло. Если ты вместо того, чтобы сделать саму операцию кладёшь что-то в очередь, то и результат функции меняется — вместо "что-то произошло", будет "что-то произойдёт".

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

Это не я придумал, это ты придумал.

P>На самом деле нет — см функциональные и нефункциональные требования.

P>Зачем вам менять апишку для этого кейса?
Затем, что функция API "выполнить действие X" != "положить в очередь чтобы действие X когда-нибудь выполнилось (может быть)". Апишка просто обязана быть разной, чтобы не было таких "чудесных вещей", которые ты так любишь.

P>>>

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

P>·>Я тебе в N+5й раз повторяю. Твоя формулировка неоднозначна. Напиши чётко.
P>Я не в курсе, что именно вам непонятно
P>Какой вопрос вы не озвочили — мне неизвестно.
P>На мой взгляд что надо в моем ответе есть
P>1. кто тестирует
Из слов "паттерн тестируется" неясно кем.

P>3. какими тестами

Из слов "паттерн тестируется" неясно какими тестами.

P>>>Структурная эквивалентность запроса — все что надо, было показано. Вам десяток страниц кода билдера фильтров показать или что?

P>·>Было показано deep.eq(..."some string"...), т.е. буквальное, посимвольное сравнение текста sql-запроса. Неясно где какой тут паттерн который ты "объясняешь", но код не показываешь.
P>проверка паттерна посимвольным сравнением
P>1. что делаем — сравниваем посимвольно, т.к. другого варианта нет — на одну задачу пилить ast на алгебраических типах данных это овердохрена
Другой вариант я рассказал — выполнить запрос на базе и заасертить результат.

P>2. для чего делаем — что бы зафиксировать паттерн

Зачем?

P>>>Его нужно применять ради решения конкретных проблем, а не лепить вообще везде. Одна из проблема — те самые фильтры.

P>·>...главное — неясно зачем писать такой хрупкий тест, когда можно банально гонять его же на бд, ровно с теми же сценариями и ассертами (ок, может чуть медленнее).
P>Я ж вас попросил — покажите тест, который на неизвестной комбинации фильтров будет гарантировано вгружать в память не более 10 строк.
P>Вы до сих пор такой тест не показали
Это не покрывается тестами принципиально.
Только если у тебя какой-то очень частый случай и ты завязываешься на какую-то конкретную реализацию — а это значит, что у тебя тесты не будут тестировать даже регрессию.

P>>>Паттерн — потому, что название даётся по назначению. Назначение этого подхода — проверять построеный запрос на соответствие паттерну.

P>·>Словоблудие.
P>Вы в который раз просите объяснений, и тут же всё это обесцениваете.
Я никогда не просил объяснений, я просил код. Ибо по коду видны факты, а не твои фантазии. Так что не удивляйся, что твои объяснения являются словоблудием. Просто объявить "этот код будет проверять построеный запрос на соответствие паттерну" — этого мало. Надо чтобы написанный код действтительно это делал. У тебя же код тупо проверяет sql-текст запроса и имена приватных методов. Никаих паттернов нет, как бы тебе этого ни хотелось.

P>Зачем вы вообще сюда пишете, показать, что у вас адекватных, релевантных аргументов нет, или что "в интернете ктото неправ" ?

Чтобы обсуждать факты, а не фантазии.

P>>>Если у вас есть более эффективный вариант, например, json-like или AST на алгебраических типах данных, будет еще лучше

P>·>Как это будет выглядеть в коде?
P>Примерно так:
P>
P>const request = create(
P>    User, 
P>    u => u.many(q => q.filter(f => f(filterExpression))
P>                      .orderBy(o => u('created'))
P>                  .top(10)
P>    )
P>);
P>

P>В результате этот request вы сможете сравнить с другим, как целиком, так и частично.
P>Например, у нас здесь простой паттерн — выбрать десять последних с простым фильтром.
P>Например, для такого паттерна я могу написать тест, который будет ограничивать filterExpression простыми выражениями
P>Можно ограничить таблицы, из которых может или не может делаться джойн итд
Т.е. в тесте ты единственное что можешь зафиксировать, что в коде у тебя действительно стоит .top(10). И что? Зачем? Наличие .top(10) и так видно, оно уже и так явно прописано в коде.
Чем тест поможет? В лучшем случе — тем, что если кто-то случайно удалит .top(10) в коде, но забудет в тесте?
Но если тебе лично так хочется для личного спокойствия, то такое и с моками можно заасертить "в коде есть такая деталь реализации".

P>>>Очень смешно. Как ваш пример экстраполировать на полторы сотни свойств разного рода — колонки, свойства внутри джсон колонокк, таблиц которые могут джойнами подтягиваться?

P>>>Вы ранее говорили — 30 значений положить в бд. Это значит, что 120 свойств будут незаполнеными, и мы не узнаем или ваш запрос фильтрует, или же хавает пустые значения которые в проде окажутся непустыми.
P>·>Я имею в виду положить 30 записей. Фильтрами потом выбираем из этих 30 и проверяем, что выбраны только те записи, которые должны попадать под данный фильтр. У каждой записи может быть сколь угодно свойств.
P>Кто вам скажет, что в ваших этих 30 записях есть все потенциально опасные или невалидные комбинации?
Это не решается тестами, никакими.

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

P>Вручную всё равно придется проверять, это обязательный этап — с этого всё начинается.
Почему обязательный?

P>·>Давай теперь по каждому из этих пунктов, как это валидируется в твоих интеграционных тестах.

P>Эта часть же как и у вас. Это именно интеграционный тест репозитория.
Т.е. тест репо+бд? У вас есть и такие тесты?

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

Потому что юнит-тесты побуквенного сравнения sql-текста практически бесполезны и даже вредны.

P>>>А вот всё множество комбинаций в фильтрах нужно покрывать совсем другими тестами.

P>·>Т.е. если одна из комбинаций фильтров нарушит, скажем пункт 8, то ты об этом узнаешь только в проде от счастливых юзеров.
P>А у вас как будет? Сколько процентов комбинаций вам известно и сколько из них вы покрыли интеграционным тестом?
P>Вы только недавно жаловались, что у вас покрывается внятно только 1% кейсов
Быстрые интеграционные тесты с замоканными внешними зависимостями, которые покрывают как можно ближе к 100% интеграции между компонентами, но не все комбинации логики отдельных компонент. Это обязанность других тестов, ещё более быстрых.

P>Поскольку комбинаций фильтров может быть на порядки больше, чем вы можете позволить себе покрыть тестами, то:

... то похрен, т.к. разговор идёт о тестах, оффтоп.

P>>>2 вы подкидываете небольшое количество значений. Смотрите пример фильтра с васями, только не ваш, а мой

P>·>Подкидывай большое число значений. Разрешаю.
P>Проблемные комбинации вам неизвестны.
P>Что будете в тест писать ?
В тесте по определению тестируется известное. Если ты считаешь твои тесты тестируют неизвестное — ты заблуждаешься.

P>>>`конский запрос LIMIT 10`

P>·>Согласен, иногда такое может сработать, не помню как оно себя поведёт с какими-нибудь union... Да ещё и в разных базах этот самый LIMIT может выглядеть по-разному. А как ты будешь отличать "тянут больше одной строки"?
P>Это же в операции известно, какие данные она возвращает. Если users.byId() то очевидно здесь один. А если users.where — вот тут а хрен его знает
Угу, именно, что "очевидно", но кто-то накосячил с фильтром в byId и у тебя внезапно не один... Т.е. если это и как-то проверяется, но не тестами.

P>>>Т.е. это просто пост условие, и давать будет ровно те же гарантии.

P>·>Неясно как это согласуется с бизнес-требованиями.
P>У вас что, в требованиях позволено сдыхать при процессинге?
http 4xx или 5xx. Везде тупо возвращать только первые 10 записей и делать вид что так и надо — это какая-то студенческя поделка.

P>>>Теорема Райса про алгоритмы и свойства функций, а не про код.

P>·> — код это один из видов формальной записи алгоритмов.
P>Если пользоваться вашим пониманием т. Райса, то например тесты это тот алгоритм, которому нужно выяснить наличие свойств у функции system-under-test
P>И тут мы узнаем... что тестами ничо не сделаешь. Гы-гы.
Тесты могут проверять только тривиальные свойства. В данном случае "код для входных данных X выдал результат Y и уложился во время T". Ты же утверждаешь, что твои тесты проверяют нетривиальные свойства вида "на неизвестных проблемных комбинациях не падает по OOM". Ты заблуждаешься, это теоретически невозможно.

P>Как только вам это стало понятно, нужно вспомнить, что тестами вы выражаете свои собственные ожидания. Даже если вы их взяли из спеке — интерпретация все равно ваша собственная, и вы в тестах фиксируете свои ожидания от того, как в каких случаях работет система

P>Соотвественно, гарантии у вас будут не с т.з. т.Райса, а с т.з. вероятностей
P>- функция которая проходит тесты по таблице истинности подходит лучше, чем та, которая эти тесты не проходит
Мде... Ещё и вероятности пришли.

P>>>Поэтому вполне логично добавить пост-условие.

P>·>Ну добавь. Тесты тут не причём. Сразу замечу, что тестировать наличие постусловий — это уже лютейший бред.
P>У вас всё бред, чего вы не понимаете
Я понимаю на шаг вперёд.

P>>>Вы то помните, то не помните. В данной задаче — просто сравнением sql. Назначение теста — зафиксировать паттерн.

P>·>Назначение теста — это твои фантазии. Суть теста — это то что он делает по факту. А по факту он сравнивает побуквенно sql, а паттерны запроса и прочие страшные слова — это пустое словоблудие.
P>Давайте проверим. Покажите мне пример, как изменение паттерна, структуры запроса будет зеленым в таком тесте.
Не понял к чему такой вопрос. Я наоборт утверждаю, что любое мелкое изменение реализации сломает тест, даже если это небольшой рефакторинг или тупо стиль. В этом и проблема такого твоего теста, он хрупкий. Часто падает когда не надо и иногда не падает когда надо, т.к. тестирует внутренние детали реализации, а не ожидания.

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

P>·>Ты опять врёшь. Вот моя настоящая цитата: "Достаточно иметь одно значение и слать разные фильтры.". Открой мой код и прочитай его ещё раз. Внимательно.
P>Это еще слабее тест. С одним значением мы не в курсе, а не вгружает ли ваш фильтр секретные данные каким либо джойном
А контекст той цитаты ты тоже не прочитал. Открой блин и прочитай наконец-то что тебе пишут.

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

P>·>Ок, переформулирую. Сложность у нас растёт ровно так же, как и у вас.
P>В том то и дело, что растет экспоненциально.
И что ты этим хочешь сказать?

P>·>Ты написал: "паттерн тестируется на бд, далее — интеграционными тестами". Я спрашиваю: как паттерн тестируется на бд _перед_ интеграционными тестами?

P>Первые запуски — руками. Потом автоматизируете и получаете интеграционный тест.
Зачем руками-то? В чём такая необходимость? И почему только первые запуски?

P>>>Вот видите — это у вас времени на тесты не хватает, т.к. логику да комбинации вы чекаете на реальной бд

P>·>На реальной бд мы чекаем только связку бд+репо, т.е. код персистенса, а не логику и комбинации.
P>Вы совсем недавно собирались все комбинации тестировать, разве нет?
Не все e2e комбинации, а комбинации конкретно данного куска кода, sut.

P>Если нет, то ваши же слова

P>"если одна из комбинаций фильтров нарушит, скажем пункт 8, то ты об этом узнаешь только в проде от счастливых юзеров."
Потому что это уже другая подсистема, не та которая конкретно отвечает за комбинации фильтров. Слон — по кусочкам.. помнишь?

P>>>У вас комбинаций меньше чем условий в фильтре.

P>·>30 это было число записей, которые могут обеспечить 230 комбинаций. Тебе столько не хватает?
P>Фильтры дают на десяток порядков больше комбинаций. В этом и проблем
Ну сделай 60 записей, будет как раз на десяток порядков больше комбинаций. В чём проблема-то?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 04.03.2024 14:09 · . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.