Здравствуйте, Pauel, Вы писали:
>> В реальности интеграцией можно покрыть от силы 1% кейсов.
P>Помните? Вот здесь вы написали обоснование тем самым тестам на проде.
Я здесь писал про интеграционные тесты вида "когда у нас дошла очередь интеграционного тестирования, где (медленно!) тащатся реальные данные из реальной базы, выполняется (медленный!) UI workflow с навигацией от логина до этого отчёта, и т.п.". И 1% тут получается не потому что нам лень покрывать всё на 100%, а потому что эти тесты медленные, т.е. 100% будет требовать в 100 раз больше времени, чем 1%. А с учётом праздников и выходных это означает, что вместо получаса это выльется в неделю.
P>Оставшиеся 99% ваши моки не заткнут, как бы вы не старались. Хоть обпишитесь.
Именно это и есть основное предназначение моков — ускорять процесс тестирования.
P>И даже если вы обмажетесь всеми возможными юнит-тестами, моками, какими угодно — покрытие интеграционными тестами в 1% говорит о том, что за продом нужно наблюдать более серьезными методами, нежели ваши health check.
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 тест будет один или около того, только одна конкретная комбинация. "Vanilla Buy Call, Auto Price, Execute, B2B Book -> assert trade reports, assert UI". Все остальные комбинации — должны быть покрыты быстрыми тестами.
P>Если же вы хотите заткнуть 99% непокрытых юзкейсов мониторингом на основе с healthcheck — чтото здесь не сходится.
Ты не понял что делает healthcheck.
P>>>Вы пока ошибок не показали. Основная ваша придирка в том, что структурная эквивалентность не нужна, хрупкая, итд — при этом альтернативы такому решению у вас нет.
P>·>Это и есть ошибка. Плюс показал потенциальные ошибки например с дефолтным значением vs null. Альтернативу тоже показал. Происто ты проигнорировал.
P>Вы показали теоретически возможные ошибки при построении запроса "а вдруг не учтем дефолтное значение". Ну так это и у вас будет — если чего то не учтете.
Если чего-то не учтёте — та же проблема и у вас. Я говорю о другом. Ты вроде учёл "а что если поле null" — и подставил nullзначение в своём pattern-тесте и прописал ожидания, тест зелёный. Но ты не учёл, что реальная субд null заменяет null на дефолтное значение и ведёт не так как ты предположил в своём тесте. А прогонять полные тесты "где (медленно!) тащатся реальные данные" для каждого поля где потенциально может быть null — ты не дождёшься окончания.
P>Ваша альтернатива не работает — упираемся в конское количество колонок, связей и разнообразия свойств внутри джсона — так вот всё хранится.
P>И ваш подход к тестированию фильтров не палит.
Режь слона на части, тогда всё запалит.
P>>>А вот изменение имени пользователя спустя секунду после создания — очень даже адекватно.
P>·>Ты путаешься в показаниях. Ты сам-то адекватен? Откуда взялось "50мс"??! Ты тут сказал, что синхронное сохранение внезапно заменил на асинхронное и у тебя возникли проблемы с тем, что теперь другой метод можно позвать только через 5 минут.
P>Я такого не говорил — это вы начали фантазировать, что придется ждать 5 минут и никак иначе не выйдет. Обоснования у вас никакого нет.
Про 5 минут я не фантазировал, это твоя цитата.
P>>>public ssl key — ничего военного. Пересоздавать этот кей спустя секунду после создания занятие абсолютно бессмысленное. А вас послушать, так окажется что любые данные ассоциированые с пользователем могут меняться сразу же, включая гуиды ссылок на любую глубину связности.
P>·>Я не знаю что это и какое это всё имеет отношение к обсуждаемому.
P>Я вам привел пример, что данные разделяются на те, что нужны сейчас, и те, что нужны потом, когда нибудь. Так не бывает, что всё всегда нужно сразу на любую глубину вложенности.
P>Если у вас хватает капасити обработать всё за один реквест — отлично. Самый простой вариант, без какого либо усложения архитектуры.
P>Как только вашего капасити перестает хватать — см требованиия, там написано, что из данных юзера нужно сразу, а что нет.
Да похрен на архитектуру, главное — это означает, что какие-то операции временно перестанут работать как раньше, меняется happens-before гарантии. Появляется необходимость синхронизации, т.е. функциональное изменение, модификация API, документации и т.п. Тесты ну просто обязаны грохнуться!
P>·>Ты пишешь бред. Я задаю вопрос, ты игнорируешь и снова повторяешь то же самое. Попробуем ещё раз:
P>·>Я читаю что написано. В цитате выше написано "тестируется" (само как-то?), потом стоит запятая и слово "далее" и про интеграционные тесты. Если эта твоя цитата не говорит что ты хочешь сказать, переформулируй, у меня очень плохо с телепатией.
P>Цитирую себя N+3й и N+4й разы — выделил
P>P>п1 — паттерн тестируется на бд, далее — интеграционными тестами
P>...
P>1. рабочий паттерн запроса — это проверяется сначала руками, а потом тестом против бд минимально заполненой под задачу
Я тебе в N+5й раз повторяю. Твоя формулировка неоднозначна. Напиши чётко.
P>>>Пример недостаточно информативный. А вот сам подход — проверять построение запроса — очень даже хороший.
P>·>Т.е. код не был даден. ЧТД. Куда отматывать-то?
P>Структурная эквивалентность запроса — все что надо, было показано. Вам десяток страниц кода билдера фильтров показать или что?
Было показано deep.eq(..."some string"...), т.е. буквальное, посимвольное сравнение текста sql-запроса. Неясно где какой тут паттерн который ты "объясняешь", но код не показываешь.
P>>>Какого угодно. Если вам для запроса данных нужна цепочка джойнов или вложеный запрос — то это и есть паттерн.
P>·>Это ты цель со средством путаешь. Мне надо чтобы запрос выдавал результат который нужен по спеке. А что там — цепочка джойнов или чёрт лысый — абсолютно пофиг. Более того, если сегодня написали запрос с джойнами, а завтра, после разговора с dba его переписали на вложенный — то ни один тест упасть не должен.
P>Я ж вам сразу сказал — такой тест более хрупкий.
Не, ты это не говорил. Но не важно...
P>Его нужно применять ради решения конкретных проблем, а не лепить вообще везде. Одна из проблема — те самые фильтры.
...главное — неясно зачем писать такой хрупкий тест, когда можно банально гонять его же на бд, ровно с теми же сценариями и ассертами (ок, может чуть медленнее).
P>>>А так же надо учесть вещи вида "не делает то чего делать не должен".
P>·>Мы говорим о тестах. Как тебе тесты помогут что-то гарантировать? Причём тут тесты??!
P>Тесты пишутся именно ради гарантий:
P>1 делает что надо
P>2 не делает того, чего не надо
Так и я показал как учитывать такие вещи в тестах репо+бд.
P>>>Все что передается в eq — паттерн для теста. А ' наш конкретный запрос' тут проверяем паттерн запроса к бд.
P>·>Здесь в коде это просто строка полного sql-запроса, которая сравнивается по equals. Я не понимаю почему ты это называешь паттерном, что в тут паттернится-то?
P>Паттерн — потому, что название даётся по назначению. Назначение этого подхода — проверять построеный запрос на соответствие паттерну.
Словоблудие.
P>Если у вас есть более эффективный вариант, например, json-like или AST на алгебраических типах данных, будет еще лучше
Как это будет выглядеть в коде?
P>>>Вы показали примитивные тесты вида "положили в таблицу, проверили, что нашли положенное". А как протестировать "не нашли неположенного" или "не начали вгружать вообще всё" — вы такого не показали.
P>·>Показал. Ещё раз разжую. Минимальный пример: Если мы в таблицу кладём "Vasya", то при поиске, например по шаблону по startsWith("Vas") — должны найти одну запись, а по startsWith("Vaz") — ноль. Второй тест как раз и ассертит, что твой этот фильтр не вгружает всё, а только то, что должно было заматчиться. Ещё раз. Одна запись в БД — это 21 вариантов результата, которые можно различить.
P>Очень смешно. Как ваш пример экстраполировать на полторы сотни свойств разного рода — колонки, свойства внутри джсон колонокк, таблиц которые могут джойнами подтягиваться?
P>Вы ранее говорили — 30 значений положить в бд. Это значит, что 120 свойств будут незаполнеными, и мы не узнаем или ваш запрос фильтрует, или же хавает пустые значения которые в проде окажутся непустыми.
Я имею в виду положить 30 записей. Фильтрами потом выбираем из этих 30 и проверяем, что выбраны только те записи, которые должны попадать под данный фильтр. У каждой записи может быть сколь угодно свойств.
P>>>И то, и другое вашими примитивными тестами не решается.
P>·>Решается.
P>
P>Нисколько. Если вы положили в таблицу Vasya, то к нему нужно подкинуть полторы сотни так или иначе связанных с ним свойств. Не 30, как вам кажется, а минимум 150. И желательно, что бы таких васей было больше одного.
Ну да. И? Можешь сформировать как тестовый объект vasya и для него вызвать тот же repo.save(vasya). Т.е. вставляешь 30 вась в базу и фильтруешь их в хвост и гриву.
P>Фильтр будет не startwith, а
P>1. конское количество условий — приоритеты, скобки
P>2. паттерны строк что само по себе хороший цирк
P>3. джойны в зависимости от условий итд
P>4. приседания с джсон разной структуры
P>5. группировки, агрегирование, итд
Ну это всё ты вроде сказал уже и так будет в твоих паттерн-тестах. Суть в том, что вместо тупого побуквенного сравнения sql-текста, они этот самый sql-текст выполнят на базе из 30и записей и заасертят выданный список.
P>Например — найти всех вась которые между июнем и июлем делали повторные возвраты товаров из перечня которые были куплены между январем и мартом оформлены менеджерами которые были наняты не позже декабря прошлого года и получив % от сделок уволились с августа по сентябрь
P>Еще интереснее — найти все товары, которые двигались во всей этой кунсткамере.
И? А ты что предлагаешь? Запустить эту колбасу вручную на прод-базе и глазками проверить выдачу, что вроде похоже на правду?
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, то ты об этом узнаешь только в проде от счастливых юзеров.
P>>>Вы только что "показали" — для проверок комбинций использовать интеграционный тест репо+бд
P>·>Почему нет-то?
P>Потому, что
P>1 это медленно
Согласен, медленнее, т.к. лезет в бд. Но бд может выполнять тысячи запросов в секунду.
P>2 вы подкидываете небольшое количество значений. Смотрите пример фильтра с васями, только не ваш, а мой
Подкидывай большое число значений. Разрешаю.
P>>>Могу. Например, для тестов которые тянут больше одной строки могу просто затребовать `LIMIT 10`.
P>·>Как?
P>Вот так:
P>`конский запрос LIMIT 10`
Согласен, иногда такое может сработать, не помню как оно себя поведёт с какими-нибудь union... Да ещё и в разных базах этот самый LIMIT может выглядеть по-разному. А как ты будешь отличать "тянут больше одной строки"?
P>И добавить подобное в тесты всех критических запросов.
P>Т.е. это просто пост условие, и давать будет ровно те же гарантии.
Неясно как это согласуется с бизнес-требованиями.
P>>>Не я, а вы. Это у вас комбинации покрываются repo+db тестами. А мне достаточно гарантировать наличие свойства запроса. см пример с LIMIT 10 выше. И это свойство сохранится на все времена, а потому проблема останова для меня здесь неактуальна.
P>·>Гарантировать наличие любого нетривиального свойства у кода (а запрос — это код) — алгоритмически неразрешимая задача (т. Райса), значит тесты тут никоим образом не помогут.
P>Теорема Райса про алгоритмы и свойства функций, а не про код.
— код это один из видов формальной записи алгоритмов. А функции (ты наверно имеешь в виду чрф) эквивалентны алгоритмам по тезису Тьюринга-Чёрча. Ок... С натяжкой можно сказать, что sql не является тьюринг-полным (обычно), поэтому по его коду можно иногда определить его некоторые свойства.
P>Забавно, что вы только что сами себе рассказали, почему любые ваши тесты без толку. Только такого рода гарантий тестами вообще никто никогда не ставит.
Поправил, чтобы верно стало. Я это давно тебе пытаюсь объяснить. Наконец-то ты начал чего-то подозревать.
P>>>Я уже привел кучу примеров. А вы всё никак.
P>·>Нигде не было неизвестных комбинаций параметров, не ври.
P>Это очевидно — никто не даст вам списка "вот только с этими комбинациями не всё в порядке". Вы будете узнавать о той или иной комбинации после креша на проде.
P>Поэтому вполне логично добавить пост-условие.
Ну добавь. Тесты тут не причём. Сразу замечу, что тестировать наличие постусловий — это уже лютейший бред.
P>>>Зачем contains ? Проверяться будет весь паттерн запроса, а не его фрагмент.
P>·>Как именно?
P>Вы то помните, то не помните. В данной задаче — просто сравнением sql. Назначение теста — зафиксировать паттерн.
Назначение теста — это твои фантазии. Суть теста — это то что он делает по факту. А по факту он сравнивает побуквенно sql, а паттерны запроса и прочие страшные слова — это пустое словоблудие.
P>>>Не работает. Фильтры могут тащить из херовой тучи таблиц, связей итд. Вам надо самое меньшее — заполнение всех возможных данных, таблиц, связей и тд. И так на каждый тест.
P>·>Работает. И не на каждый тест, а на прогон практически всех тестов.
P>Т.е. вы уже меняете подход к тестам — изначально у вас было "записать три значения в бд и проверить, что они находятся фильтром"
Ты опять врёшь. Вот моя настоящая цитата: "Достаточно иметь
одно значение и слать
разные фильтры.". Открой мой код и прочитай его ещё раз. Внимательно.
P>>>У вас здесь сложность растет экспоненциально.
P>·>Не растёт.
P>Растет. Докинули поле — нужно докинуть данных на все случаи, где это будет играть, включая паттерны строк, джойны, итд итд.
Ок, переформулирую. Сложность у нас растёт ровно так же, как и у вас.
P>>>Ну сейчас то понятно, что интеграционные никто не отменял, или еще месяц-два будете это игнорировать?
P>·>Я задал вопрос. Ты его удалил не ответив.
P>Цитирую себя N+5й и N+6й разы — выделил
Ты написал: "паттерн тестируется на бд, далее — интеграционными тестами". Я спрашиваю: как паттерн тестируется на бд _перед_ интеграционными тестами?
P>1. рабочий паттерн запроса — это проверяется сначала руками, а потом тестом против бд минимально заполненой под задачу
Каким именно тестом? Как этот тест выглядит и какой код этот тест покрывает?
P>>>100к юзкейсов протестировать сложно, а написать легко? Чтото тут не сходится.
P>·>Ты откуда нателепатировал "сложно/легко"? Я написал про скорость прогона тестов.
P>Вот видите — это у вас времени на тесты не хватает, т.к. логику да комбинации вы чекаете на реальной бд
На реальной бд мы чекаем только связку бд+репо, т.е. код персистенса, а не логику и комбинации.
P>>>Эти 30 комбинаций разложить по таблицам и колонкам и окажется в среднем около нуля на большинстве комбинаций фильров.
P>·>Почему так окажется? "все возможные кейсы что есть в бд прода, или нам известно, что такие будут.", т.е. раскладываются различные с т.з. бизнеса сущности, а не просто "Account 1", "Account 2".
P>У вас комбинаций меньше чем условий в фильтре.
30 это было число записей, которые могут обеспечить 2
30 комбинаций. Тебе столько не хватает?