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

>>> В реальности интеграцией можно покрыть от силы 1% кейсов.

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

Будут. Но их всё равно нужно делать, т.к. никакие другие виды тестов их не заменяют, только кое где могут скомпенсировать.
Например — если логика сложная, а воркфлоу простой, то можно забить на e2e и заложиться на юнит-тесты.
Если у вас сложный воркфлоу, то его нечем протестировать, кроме вот этих e2e

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

·>Именно это и есть основное предназначение моков — ускорять процесс тестирования.

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

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

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

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

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

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

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

·>Ты не понял что делает healthcheck.

Это ж не я пытаюсь заменить 99% e2e тестов моками.

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

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

Это полные тесты, где всё тащится медленно и реально — они нужны и вам, и мне. Если вы не учли этот кейс с нулём, то будет проблема. Ровно так же и у меня.

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

P>>И ваш подход к тестированию фильтров не палит.
·>Режь слона на части, тогда всё запалит.

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

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

·>Про 5 минут я не фантазировал, это твоя цитата.

Я нигде не говорил, что юзер должен ждать 5 минут. Даже если профиль создаётся неделю, редактировать данные можно сразу.
Вам нужно спросить у BA, какие данные надо сейчас, а какие — вообще.

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

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

Вы похоже так и не поняли, что такое функциональные требования, а что такое нефункциональные.
Пример:
Функциональные — админ может создать спейс для юзера, админ может отредактировать профиль юзера.
Нефункциональные — профиль юзера можно редактировать сразу после создания. Профиль юзера создаётся за 100 мсек. админ не должен ждать чего то там.

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

Зачем вам менять апишку для этого кейса?

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

P>>Цитирую себя N+3й и N+4й разы — выделил
P>>

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

·>Я тебе в N+5й раз повторяю. Твоя формулировка неоднозначна. Напиши чётко.

Я не в курсе, что именно вам непонятно
Какой вопрос вы не озвочили — мне неизвестно.
На мой взгляд что надо в моем ответе есть
1. кто тестирует
2. на чем тестирует
3. какими тестами
4. какая бд и чем заполнена

Что вам в этом ответе непонятно? Что конкретно? Вы вопрос внятно сформулировать можете или мне за вас это делать четвертый месяц?

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

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

проверка паттерна посимвольным сравнением
1. что делаем — сравниваем посимвольно, т.к. другого варианта нет — на одну задачу пилить ast на алгебраических типах данных это овердохрена
2. для чего делаем — что бы зафиксировать паттерн

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

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

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

P>>Тесты пишутся именно ради гарантий:

P>>1 делает что надо
P>>2 не делает того, чего не надо
·>Так и я показал как учитывать такие вещи в тестах репо+бд.

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

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

·>Словоблудие.

Вы в который раз просите объяснений, и тут же всё это обесцениваете.
Зачем вы вообще сюда пишете, показать, что у вас адекватных, релевантных аргументов нет, или что "в интернете ктото неправ" ?

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

·>Как это будет выглядеть в коде?

Примерно так:
const request = create(
    User, 
    u => u.many(q => q.filter(f => f(filterExpression))
                      .orderBy(o => u('created'))
                  .top(10)
    )
);


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

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

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

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

P>>Например — найти всех вась которые между июнем и июлем делали повторные возвраты товаров из перечня которые были куплены между январем и мартом оформлены менеджерами которые были наняты не позже декабря прошлого года и получив % от сделок уволились с августа по сентябрь

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

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

P>>Например, репозиторий Users метод create — параметры — данные юзера, возвращаемое значение — созданный юзер + ассоциированные данные. Нас интересует

P>>1. разложит данные юзера в соответствии со схемой, подкинет гуид где надо
P>>2. построит запрос один или несколько, сколько надо для той бд-сервиса-итд, что в конфигурации
P>>3. отшлет вбд и вернет данные с запрошеными ассоциациями, иды, гуиды — будут соответсвовать схеме, входным данным итд
P>>4. подклеит метадату
P>>5. повторый запрос с тем же гуид должен зафейлиться с конкретной ошибкой
P>>6. такой же запрос с другим гуид должен выполнится хорошо
P>>7. корректно пробрасывать ошибки вида "дисконнект бд из за рестарта"
P>>8. сумеет в опции — ничего не тащить из бд если был флаг write-only
·>Давай теперь по каждому из этих пунктов, как это валидируется в твоих интеграционных тестах.

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

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

·>Т.е. если одна из комбинаций фильтров нарушит, скажем пункт 8, то ты об этом узнаешь только в проде от счастливых юзеров.

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

Поскольку комбинаций фильтров может быть на порядки больше, чем вы можете позволить себе покрыть тестами, то:
1 подкинули флаг и выхлоп пустой — интеграционный тест, по одному на самые частые паттерны запросов
1. update — возвращаем пусто если write-only, или новое состояние
2. get — кидаем исключение, если write-only
3. итд
2 пост-условие — выхлоп пустой — это не тест, а именно пост-условие. Та часть которая вам непонятна последние 4 месяца
3 логирование — выхлоп игнорируется, если есть, с записью в лог, далее — всплывет в мониторинге при первом же случае.
4 дизайн — пре-, и пост-процессинг определяется маппером, а не набором if. Маппер тестируем юнит тестами.

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

·>Подкидывай большое число значений. Разрешаю.

Проблемные комбинации вам неизвестны.
Что будете в тест писать ?

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

·>Согласен, иногда такое может сработать, не помню как оно себя поведёт с какими-нибудь union... Да ещё и в разных базах этот самый LIMIT может выглядеть по-разному. А как ты будешь отличать "тянут больше одной строки"?

Это же в операции известно, какие данные она возвращает. Если users.byId() то очевидно здесь один. А если users.where — вот тут а хрен его знает

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

·>Неясно как это согласуется с бизнес-требованиями.

У вас что, в требованиях позволено сдыхать при процессинге?
1 Нужно обработать весь объем данных
2 дизайн под обработку частями
3 запросы в соответствии с дизайном
4 тесты запросов на соответствие с дизайном, решает вопрос с "я быстренько, там всё просто"
5 код ревью "не выключил ли кто тесты а б и цэ"

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

Если пользоваться вашим пониманием т. Райса, то например тесты это тот алгоритм, которому нужно выяснить наличие свойств у функции system-under-test
И тут мы узнаем... что тестами ничо не сделаешь. Гы-гы.

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

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

·>Поправил, чтобы верно стало. Я это давно тебе пытаюсь объяснить. Наконец-то ты начал чего-то подозревать.

Вам стоило бы почитать Гленфорда Майерса — он объясняет подробно, что к чему, откуда какие гарантии берутся

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

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

У вас всё бред, чего вы не понимаете

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

·>Назначение теста — это твои фантазии. Суть теста — это то что он делает по факту. А по факту он сравнивает побуквенно sql, а паттерны запроса и прочие страшные слова — это пустое словоблудие.

Давайте проверим. Покажите мне пример, как изменение паттерна, структуры запроса будет зеленым в таком тесте.

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

·>Ты опять врёшь. Вот моя настоящая цитата: "Достаточно иметь одно значение и слать разные фильтры.". Открой мой код и прочитай его ещё раз. Внимательно.

Это еще слабее тест. С одним значением мы не в курсе, а не вгружает ли ваш фильтр секретные данные каким либо джойном

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

·>Ок, переформулирую. Сложность у нас растёт ровно так же, как и у вас.

В том то и дело, что растет экспоненциально.

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

Первые запуски — руками. Потом автоматизируете и получаете интеграционный тест.

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

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

Вы совсем недавно собирались все комбинации тестировать, разве нет?
Если нет, то ваши же слова
"если одна из комбинаций фильтров нарушит, скажем пункт 8, то ты об этом узнаешь только в проде от счастливых юзеров."

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

·>30 это было число записей, которые могут обеспечить 230 комбинаций. Тебе столько не хватает?

Фильтры дают на десяток порядков больше комбинаций. В этом и проблем
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.