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 · . Предыдущая версия .
Re[87]: Что такое Dependency Rejection
От: Sinclair Россия https://github.com/evilguest/
Дата: 05.03.24 16:59
Оценка:
Здравствуйте, ·, Вы писали:


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

P>>·>Про 5 минут я не фантазировал, это твоя цитата.
P>>Я нигде не говорил, что юзер должен ждать 5 минут. Даже если профиль создаётся неделю, редактировать данные можно сразу.
·>Говорил, цитирую: чудесные вещи типа "после того, как сделал var u = service.CreateUser(...), нужно выждать не менее 5 минут перед service.UpdateUser(u, ...)".
Это я говорил. Это — реальный случай: парни, которые реализовывали API, завели его в ActiveDirectory с round-robin.
Объект "пользователь" в ихнем волшебном API создавался с "автосгенерённым паролем", коий предлагалось как-то заперсистить и донести до конечного пользователя.
С нашей стороны, конечно же, сразу была сделана обвязка этого API, которая принимала пароль от пользователя вместе со всеми остальными параметрами, и создавала объект в два вызова — CreateUser и UpdatePassword.
С моками всё работало прекрасно. И в первом же e2e тесте упало, потому что неумолимый round-robin роутил UpdatePassword на другой AD-контроллер, на котором свежесозданного user ещё нет, и нам прилетел 404.
А вот если подождать 5 минут, то скорее всего UpdatePassword сработает, потому что пройдёт репликация AD, и все контроллеры этого пользователя увидят.
Как вы такие вещи будете ловить при помощи моков — я так и не понял.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[88]: Что такое Dependency Rejection
От: · Великобритания  
Дата: 06.03.24 08:34
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>·>Говорил, цитирую: чудесные вещи типа "после того, как сделал var u = service.CreateUser(...), нужно выждать не менее 5 минут перед service.UpdateUser(u, ...)".

S>Это я говорил.
Ой, прошу прощения, я перепутал.

S>Это — реальный случай: парни, которые реализовывали API, завели его в ActiveDirectory с round-robin.

Перепутал наверное потому, что эти парни с той же логикой что и у Pauel — подменили синхронный код асинхронным с eventual consistency — и делают вид что всё в порядке, так и должно работать, назвали это как "нефункциональное требование" в API, и никакие тесты у них наверняка не упали.

S>А вот если подождать 5 минут, то скорее всего UpdatePassword сработает, потому что пройдёт репликация AD, и все контроллеры этого пользователя увидят.

Да, именно что "скорее всего". В общем явная лажа в дизайне, даже неясно как consistency обеспечить хотя бы eventual.

S>Как вы такие вещи будете ловить при помощи моков — я так и не понял.

Я же рассказал — такое ловится конформационными тестами — небольшие быстрые тесты, которые дёргают "ихнее API" и ассертят результаты, проверяют на адекватность assumptions, сделанные в моках. Ловить такое в e2e вида "выполняется (медленный!) UI workflow с навигацией от логина" — можно только в простых случаях.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 06.03.2024 8:51 · . Предыдущая версия .
Re[87]: Что такое Dependency Rejection
От: Pauel Беларусь http://blogs.rsdn.org/ikemefula
Дата: 06.03.24 11:40
Оценка:
Здравствуйте, ·, Вы писали:

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

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

И вам всё еще непонятно, почему люди на проде тестируют? моки и конформационные тесты это не решение, а компенсация.
Вы отказываетесь идти дальше, и делать внятное покрытие e2e, и посмеиваетесь с тех, кто всё таки покрывает эти 99% как следует.

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

·>Я тебе уже рассказал чем: моки и конф-тесты.

Пример у вас есть внятный? Мне непонятно, каким чудом моки репозитория помогут понять, что же с интеграцией не так.

Какой компоент системы вы собираетесь конф тестами покрывать?

P>>Не могут — моки по своей сути подменяют интеграцию отсебятиной.

·>Не только моки, но конф-тесты. Плюс соответствующий дизайн и процесс разработки.

Расскажите внятно, как мок репозитория поможет вам с интеграцией уровня приложения?

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

·>Я тебе уже объяснил как это сделать известным.

Невнятно, если честно. Общие слова "конф-тесты помогут"

P>>Итого, с ваших слов "задача сложная, нахер e2e, будем моки писать и сделаем вид, что это равноценная замена"

·>Не "нахер e2e", а "хрень получается с e2e".

Наоборот, e2e показывает те ошибки, которые проходят мимо ваших моков. Они показывают, что приложение склеено как положено.

P>>1 Аксиома — количество юнит-тестов и полуинтеграционных на моках никак не влияет на количество e2e. Никак, нисколько!

·>Тавтология. Я говорю о другом: количество e2e ограничивается доступными ресурсами. Чем меньше доступных e2e, тем больше приходится вкладываться в юнит и "полуинтеграционные".

Ну да — вы забили на интеграционные тесты.

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

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

Я ж вам не предлагаю выбросить — я вам предлагаю добавить, и показываю, как именно.
Вы уже релизитесь без 99%. Если к вашим тестами добавите e2e, то покрытие станет плотнее.
Если не хватает времени — e2e можно гонять и после деплоймента, хоть круглые сутки, они хлеба не просят.

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

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

Вы уже это делаете. e2e всего то сделают видимыми проблемы в тех 99% которые ходили мимо ваших моков.

P>>3. каждые N минут запускаете `curl <stage>/manifest.test | run-test --random --url <stage>`

·> Т.е. качество продукта проверяем случайно. Это только для хоумпейджей годится.

Это способ выполнять те тесты, которые у вас вообще отсутствуют.

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

·>Ясен пень. Ты вообще читаешь что я пишу? Суть в том, что даже если вы учли кейс с нулём, то у вас всё равно будет проблема, а нас проблемы не будет.

Непонятно — как X может быть больше чем X + Y если и X и Y больше нуля?
Вы тестируете минимально, репозиторий + бд. Эта часть есть и у меня.
Только вы здесь останавливаетесь, а я, дополнительно, покрываю другим видом тестом несколько критичных кейсов.

И по вашему у меня покрытие будет хуже

Извините, но вы или не читаете, или вас чтото не то с логикой.

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

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

Комбинации различных условий в фильтре, что дает нам ту самую data complexity, и проблемные комбинации вы будете узнавать только на проде, одну за другой

Собственно из за того, что никто вам заранее не выдал список проблемных комбинаций, нужно "придётся выдумывать правильную архитектуру и подходы контроля качества, чтобы риск неизвестного минимизировать"


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

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

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

Синклер привел пример "А в реальных системах встречаются ещё и чудесные вещи типа "после того, как сделал var u = service.CreateUser(...), нужно выждать не менее 5 минут перед service.UpdateUser(u, ...)", с которыми совсем всё плохо." И пример был про то, откуда берется время "задним числом"
Можно ли нажимать конопку, нельзя — у Синклера пример про то откуда будет браться время отличное от текущего.

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

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

Вот-вот. И если BA даёт добро — всё хорошо.

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


Не вместо, а вместе. Все что нужно для редактирования — возвращаем сразу, BA так сказал. Всё, что для редактирования не нужно — вообще не возвращаем. А раз так, то можем поступать как проще — можно сразу, можно потом.
Уже в который раз уточняю, но вы всё равно видите чтото свое

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

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

Уже выяснили — это вы притянули пример Синклера который был совсем про другое.

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

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

Вы путаете интерфейс и реализацию. API — create user, данные вернуть сразу — id, имя, фамилию. Всё не id-имя-фамилия, вы может создавать когда угодно, хоть с очередью, хоть без. Можете создать сразу — отлично. Не получается — ну и хрен с ним, абы id-имя-фамилия были дадены сразу


P>>>>

P>>>>п1 — паттерн тестируется на бд, далее —

интеграционными тестами

P>>>>...
P>>>>1. рабочий паттерн запроса — это проверяется

сначала руками, а потом тестом против бд минимально заполненой под задачу

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

Если руками, что следует из выделеного, то какие варианты?

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

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

Я вам выделил, что бы виднее было.

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

·>Другой вариант я рассказал — выполнить запрос на базе и заасертить результат.

Не работает — вы не знаете проблемной комбинации для фильтров. Знаете только что они есть и их хрензнаетсколькилион

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

·>Зачем?

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

P>>Вы до сих пор такой тест не показали

·>Это не покрывается тестами принципиально.

Покрывается. Я ж вам показал. Только решение и покрытие это разные вещи. В данном случае решение — постусловие. А тестами фиксируем, что бы не убежало

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


Покажите пример регрессии, которая не будет обнаружена этим тестом.

·>Я никогда не просил объяснений, я просил код. Ибо по коду видны факты, а не твои фантазии. Так что не удивляйся, что твои объяснения являются словоблудием.


С кодом у вас два варианта
1. не будет работать
2. словоблудие

На примере — вам был даден код про структурную эквивалентность — вам надо знать, кто тестирует, какими тестами. Пять сообщений подряд и выделение болдом не помогло. Надеюсь, выделение H1 сделало это заметным

P>>Можно ограничить таблицы, из которых может или не может делаться джойн итд

·>Т.е. в тесте ты единственное что можешь зафиксировать, что в коде у тебя действительно стоит .top(10). И что? Зачем? Наличие .top(10) и так видно, оно уже и так явно прописано в коде.

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

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

·>Это не решается тестами, никакими.

Решение — пост-условие. А вот тест который я показал, он всего то гарантирует сохранность этого пост-условия.

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

·>Почему обязательный?

Если вы не можете сделать этого руками, то ваши попытки выразить это в коде и генерации данных будут иметь ту же особенность.

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

·>Т.е. тест репо+бд? У вас есть и такие тесты?

Я вам уже который месяц говорю, что предлагаю добавить, а не выбросить.
Есть и такие — их проще писать сразу, проще отдладить по горячим следам.
Но с ростом сложности приходится вводить дополнительные инструменты.

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

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

А разве я говорю, что это надо повсеместно?

P>>Проблемные комбинации вам неизвестны.

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

Пост-условия похоже для вас пустой звук, вам это всё равно кажется тестом. Тест всего лишь гарантирует сохранность пост-условия
А вот пост условие отработаетт для всего выхлопа, сколько бы раз вы ни вызывали его

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

·>Угу, именно, что "очевидно", но кто-то накосячил с фильтром в byId и у тебя внезапно не один... Т.е. если это и как-то проверяется, но не тестами.

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

P>>У вас что, в требованиях позволено сдыхать при процессинге?

·>http 4xx или 5xx. Везде тупо возвращать только первые 10 записей и делать вид что так и надо — это какая-то студенческя поделка.

Вы снова чего то фантазируете.

P>>И тут мы узнаем... что тестами ничо не сделаешь. Гы-гы.

·>Тесты могут проверять только тривиальные свойства.

Нет, не могут. Так, как это утверждает т. Райса — вы true от false отличить не сможете. Можете потренироваться — тестами обнаружить функцию, которая всегда возвращает true. Валяйте.
Как напишете ваш тест — кидайте сюда, а я покажу, какая фукнция проходит ваш тест и не удовлетворяет условию
Для простоты, функция будет такой — () -> true, т.к. никаких параметров и тд и тд и тд
Как наиграетесь, приходите

P>>Соотвественно, гарантии у вас будут не с т.з. т.Райса, а с т.з. вероятностей

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

Они всегда были. см выше про т. Райса

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

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

Смотрите выше, текст выделен H1, как раз эту вашу способность и "демонстрирует"

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

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

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

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

P>>В том то и дело, что растет экспоненциально.
·>И что ты этим хочешь сказать?

Да то, что искать тестами проблемные комбинации вы вспотеете. Нужно искать решение вне тестов.

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

·>Зачем руками-то? В чём такая необходимость? И почему только первые запуски?

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

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

P>>Вы совсем недавно собирались все комбинации тестировать, разве нет?

·>Не все e2e комбинации, а комбинации конкретно данного куска кода, sut.

С фильтрами комбинаций столько, что солнце погаснет раньше ваших тестов.

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

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

Я то помню, а вы — нет. Смотрите, кто погаснет раньше,солнце, или перебор комбинаций на фильтрах.

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

P>>Фильтры дают на десяток порядков больше комбинаций. В этом и проблем
·>Ну сделай 60 записей, будет как раз на десяток порядков больше комбинаций. В чём проблема-то?

Проблема в том, что бы генерить данные, нужно знать комбинации которые будут проблемными.
Отредактировано 06.03.2024 19:54 Pauel . Предыдущая версия .
Re[89]: Что такое Dependency Rejection
От: Sinclair Россия https://github.com/evilguest/
Дата: 06.03.24 14:46
Оценка:
Здравствуйте, ·, Вы писали:

·>Перепутал наверное потому, что эти парни с той же логикой что и у Pauel — подменили синхронный код асинхронным с eventual consistency — и делают вид что всё в порядке, так и должно работать, назвали это как "нефункциональное требование" в API, и никакие тесты у них наверняка не упали.

Мы "этих парней" регулярно ловили с фатальными багами в проде. Причём за время моей работы на проекте на той стороне сменилось четыре поколения одного и того же API, и, судя по всему, писали каждый раз новой командой.
Я, помнится, даже предлагал им наши автотесты бесплатно, чтобы они так не позорились. Не взяли :xz:/

·>Да, именно что "скорее всего". В общем явная лажа в дизайне, даже неясно как consistency обеспечить хотя бы eventual.

Да почему — конечно ясно. Добавляется хидер "affinity key", и всё начинает работать. Как, в общем-то, они и сделали после того, как мы слегка побили их палкой за такие вещи.

·>Я же рассказал — такое ловится конформационными тестами — небольшие быстрые тесты, которые дёргают "ихнее API" и ассертят результаты, проверяют на адекватность assumptions, сделанные в моках.

Простите, но это — набор слов. Дёргать "ихнее API" мы дёргаем — просто шансы на то, что мы будем дёргать именно так, чтобы поймать на вот таких вещах, примерно нуль.
Тесты на "создание пользователя" проходят прекрасно.
Тесты на получение списка пользователей проходят прекрасно.
Тесты на модификацию пользователя, и отдельно на сброс пароля тоже прекрасно проходят (ну, мы так думаем — ведь для настоящей проверки нужно этим пользователем с этим паролем куда-то залогиниться, а у нас к этому куда-то доступа на тот момент нету).
Тесты на удаление пользователя проходят.

Более того — у "тех парней" естественно есть полный набор вот таких вот "небольших быстрых тестов".

Проблема — как раз в том, что нам важна не работоспособность методов по отдельности, а e2e-workflow.
И именно его-то мы и проверяем нашими авто-тестами. Которые как раз вполне себе "небольшие и быстрые", так что о том, что свежевыкаченная на прод версия сломана, мы узнавали примерно через полчаса.
·>Ловить такое в e2e вида "выполняется (медленный!) UI workflow с навигацией от логина" — можно только в простых случаях.
UI Workflow не намного медленнее чисто-бэкендного набора вызовов. В реальности мы делали e2e не через UI (это у нас тестировалось отдельно, селениумом), а через наш собственный API, торчаший наружу.
Но разница небольшая — потому что наш UI был просто обёрткой над нашим же API, так что если API не работает, то в UI можно даже не смотреть.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[90]: Что такое Dependency Rejection
От: · Великобритания  
Дата: 06.03.24 20:58
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>·>Да, именно что "скорее всего". В общем явная лажа в дизайне, даже неясно как consistency обеспечить хотя бы eventual.

S>Да почему — конечно ясно. Добавляется хидер "affinity key", и всё начинает работать. Как, в общем-то, они и сделали после того, как мы слегка побили их палкой за такие вещи.
Ну как исправить-то ясно, конечно. Я имел в виду, что без исправления, с таким дизайном api надёжно пользоваться невозможно.
Суть в том, что как Pauel утверждает, мол, что если заменили прямой вызов действия на асинхронную очередь — тесты не должны падать. Это ущербная логика.

S>·>Я же рассказал — такое ловится конформационными тестами — небольшие быстрые тесты, которые дёргают "ихнее API" и ассертят результаты, проверяют на адекватность assumptions, сделанные в моках.

S>Простите, но это — набор слов. Дёргать "ихнее API" мы дёргаем — просто шансы на то, что мы будем дёргать именно так, чтобы поймать на вот таких вещах, примерно нуль.
Конф-тесты дёргают "ихнее API" в ожидаемом вами порядке.

S>Проблема — как раз в том, что нам важна не работоспособность методов по отдельности, а e2e-workflow.

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

S>И именно его-то мы и проверяем нашими авто-тестами. Которые как раз вполне себе "небольшие и быстрые", так что о том, что свежевыкаченная на прод версия сломана, мы узнавали примерно через полчаса.

Ну примерно для этого конф-тесты и нужны.

S>·>Ловить такое в e2e вида "выполняется (медленный!) UI workflow с навигацией от логина" — можно только в простых случаях.

S>UI Workflow не намного медленнее чисто-бэкендного набора вызовов. В реальности мы делали e2e не через UI (это у нас тестировалось отдельно, селениумом), а через наш собственный API, торчаший наружу.
S>Но разница небольшая — потому что наш UI был просто обёрткой над нашим же API, так что если API не работает, то в UI можно даже не смотреть.
Ну как бы внезапно получилось, что не совсем e2e оказывается.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[88]: Что такое Dependency Rejection
От: · Великобритания  
Дата: 06.03.24 22:58
Оценка:
Здравствуйте, Pauel, Вы писали:

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

P>·>Нужно, но как-то всё равно придётся покрывать остальные 99%. Вот тут и приходят на помощь моки и конф-тесты.
P>И вам всё еще непонятно, почему люди на проде тестируют?
Потому что могут (с)

P>моки и конформационные тесты это не решение, а компенсация.

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

P>Вы отказываетесь идти дальше, и делать внятное покрытие e2e, и посмеиваетесь с тех, кто всё таки покрывает эти 99% как следует.

Скорее завидую простоте системы, что её реально покрыть e2e на 99%.

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

P>·>Я тебе уже рассказал чем: моки и конф-тесты.
P>Пример у вас есть внятный? Мне непонятно, каким чудом моки репозитория помогут понять, что же с интеграцией не так.
Они не для этого.

P>Какой компоент системы вы собираетесь конф тестами покрывать?

Конф-тесты тестируют чужое api. Система (по крайней мере её прод-часть) не принимает участие в конф-тестах.

P>>>Не могут — моки по своей сути подменяют интеграцию отсебятиной.

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

P>>>Итого, с ваших слов "задача сложная, нахер e2e, будем моки писать и сделаем вид, что это равноценная замена"

P>·>Не "нахер e2e", а "хрень получается с e2e".
P>Наоборот, e2e показывает те ошибки, которые проходят мимо ваших моков.
Если оно это показывает случайно, через несколько часов — то это называется хрень, а не показания.

P> Они показывают, что приложение склеено как положено.

Это и проблема твоего подхода — у тебя в приложении на строчку кода ведро клея, который никак и не тестируется, кроме как тонной e2e.

P>>>1 Аксиома — количество юнит-тестов и полуинтеграционных на моках никак не влияет на количество e2e. Никак, нисколько!

P>·>Тавтология. Я говорю о другом: количество e2e ограничивается доступными ресурсами. Чем меньше доступных e2e, тем больше приходится вкладываться в юнит и "полуинтеграционные".
P>Ну да — вы забили на интеграционные тесты.
Ну так в моём подходе этой интеграции кот наплакал, поэтому "приложение не упало при старте" уже практически означает, что 100% тех ошибок, "которые проходят мимо ваших моков" показано.

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

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

P>Вы уже релизитесь без 99%. Если к вашим тестами добавите e2e, то покрытие станет плотнее.

Не станет.

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

Зачем? Если они что-то обнаружат, это значит что у клиентов проблемы прямо сейчас, prod incident. Поздно борожоми пить.

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

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

P>>>3. каждые N минут запускаете `curl <stage>/manifest.test | run-test --random --url <stage>`

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

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

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

P>Извините, но вы или не читаете, или вас чтото не то с логикой.

Я читаю что ты мне пишешь.

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

P>·>Может я не понял твою задачу правильно, но в моём понимании, фильтры это задача которая сводится к одному компоненту, который может быть протестирован отдельно, а не комбинации различных компонент
P>Комбинации различных условий в фильтре, что дает нам ту самую data complexity, и проблемные комбинации вы будете узнавать только на проде, одну за другой
Ты тоже.

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

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

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

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

P>Синклер привел пример "А в реальных системах встречаются ещё и чудесные вещи типа "после того, как сделал var u = service.CreateUser(...), нужно выждать не менее 5 минут перед service.UpdateUser(u, ...)", с которыми совсем всё плохо." И пример был про то, откуда берется время "задним числом"

Перечитай. Проблема была не со временем, а с синхронизацией действий.

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

Причём тут текущее время? Проблема как раз была в том, что ты называешь "нефункциональные требования": "ну юзер же создался, а что ещё не совсем до конца создался, ничего страшного..".

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

P>·>Именно — вопросы за которые отвечает BA — это функциональные требования по сути.
P>Вот-вот. И если BA даёт добро — всё хорошо.
Ну вот это "добро" тебе и надо будет выразить в тестах.

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

P>Не вместо, а вместе. Все что нужно для редактирования — возвращаем сразу, BA так сказал. Всё, что для редактирования не нужно — вообще не возвращаем. А раз так, то можем поступать как проще — можно сразу, можно потом.
P>Уже в который раз уточняю, но вы всё равно видите чтото свое
В прыжке переобуваешься. Это совершенно не то, что ты изначально утверждал "БЛ начала вызываться через очередь, с задержкой от часов до дней". Написал "БЛ", а на самом деле телепатировал, есть части логики нужные сейчас, а есть части не сейчас. Если ты не понимаешь как это выражается с моками, задавай вопросы, а не фантазируй проблемы.

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

P>·>Это не я придумал, это ты придумал.
P>Уже выяснили — это вы притянули пример Синклера который был совсем про другое.
Да, я перепутал. У Синклера было про 5 минут, а у тебя было часы-дни.

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

P>·>Затем, что функция API "выполнить действие X" != "положить в очередь чтобы действие X когда-нибудь выполнилось (может быть)". Апишка просто обязана быть разной, чтобы не было таких "чудесных вещей", которые ты так любишь.
P>Вы путаете интерфейс и реализацию. API — create user, данные вернуть сразу — id, имя, фамилию. Всё не id-имя-фамилия, вы может создавать когда угодно, хоть с очередью, хоть без. Можете создать сразу — отлично. Не получается — ну и хрен с ним, абы id-имя-фамилия были дадены сразу
Это вы путаетесь в показаниях.

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

P>Если руками, что следует из выделеного, то какие варианты?
Мде, отстой.

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

P>·>Из слов "паттерн тестируется" неясно какими тестами
P>Я вам выделил, что бы виднее было.
Что за тест "против бд минимально заполненой под задачу"? Какие части системы он тестирует? e2e? или связка репо+бд? Или ещё что?

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

P>·>Другой вариант я рассказал — выполнить запрос на базе и заасертить результат.
P>Не работает — вы не знаете проблемной комбинации для фильтров. Знаете только что они есть и их хрензнаетсколькилион
Комбинация ровно та же — что и в твоём тесте. Тест почти такой же — только вместо деталей реализации ассертится поведение.

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

P>·>Зачем?
P>Что бы исключить ту самую проблему в явном виде — потенциальная загрузка всего содержимого таблицы.
Эта проблема не исключается тестом, как бы тебе это ни хотелось.

P>>>Вы до сих пор такой тест не показали

P>·>Это не покрывается тестами принципиально.
P>Покрывается. Я ж вам показал. Только решение и покрытие это разные вещи. В данном случае решение — постусловие. А тестами фиксируем, что бы не убежало
Что значит "не убежало"?

P>·>Только если у тебя какой-то очень частый случай и ты завязываешься на какую-то конкретную реализацию — а это значит, что у тебя тесты не будут тестировать даже регрессию.

P>Покажите пример регрессии, которая не будет обнаружена этим тестом.
Ну загрузится пол таблицы. Или банально твой .top(10) не появится для какой-то конкретной комбинации фильтров, т.к. для этой комбинации решили немного переписать кусочек кода и забыли засунуть .top(10).

P>·>Я никогда не просил объяснений, я просил код. Ибо по коду видны факты, а не твои фантазии. Так что не удивляйся, что твои объяснения являются словоблудием.

P>С кодом у вас два варианта
P>1. не будет работать
Так я объясняю почему не будет работать и как надо сделать чтобы работало.

P>>>Можно ограничить таблицы, из которых может или не может делаться джойн итд

P>·>Т.е. в тесте ты единственное что можешь зафиксировать, что в коде у тебя действительно стоит .top(10). И что? Зачем? Наличие .top(10) и так видно, оно уже и так явно прописано в коде.
P>Необязательно. Если я руками пишу весь запрос — то будет в коде явно. но запрос может и билдер создать.
Магически что-ли? Билдер рандомно top(10) не вставит, будет некий кусок кода который его ставит.

P>Некоторые детали нужно фиксировать в тестах, что бы случайно залетевший дятел не разрушил цивилизацию.

Дятлы тесты тоже умеют редактировать. А с учётом того, что у тебя в тестах полный текст sql — то это будет простой copy-paste и вечнозелёный тест. А на ревью определить корректность sql на вид могут не только лишь все.

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

P>·>Это не решается тестами, никакими.
P>Решение — пост-условие. А вот тест который я показал, он всего то гарантирует сохранность этого пост-условия.
Не гарантирует.

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

P>·>Почему обязательный?
P>Если вы не можете сделать этого руками, то ваши попытки выразить это в коде и генерации данных будут иметь ту же особенность.
Ты не понял. Я могу сделать это не руками, а кодом.

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

P>·>Т.е. тест репо+бд? У вас есть и такие тесты?
P>Я вам уже который месяц говорю, что предлагаю добавить, а не выбросить.
P>Есть и такие — их проще писать сразу, проще отдладить по горячим следам.
P>Но с ростом сложности приходится вводить дополнительные инструменты.
Это где ты говорил такое месяц назад? Цитату.

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

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

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

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

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

P>·>Угу, именно, что "очевидно", но кто-то накосячил с фильтром в byId и у тебя внезапно не один... Т.е. если это и как-то проверяется, но не тестами.
P>Эта проблема и у вас будет. Например, вы тестируете на бд, где id это праймари кей, а в проде каким то чудом окажется другая схема, где id может иметь дубликаты
P>И решения и у меня, и у вас, для подобных кейсов будут одни и те же.
Ну может быть колонок id несколько разных и иногда они совпадают... но не всегда.

P>>>У вас что, в требованиях позволено сдыхать при процессинге?

P>·>http 4xx или 5xx. Везде тупо возвращать только первые 10 записей и делать вид что так и надо — это какая-то студенческя поделка.
P>Вы снова чего то фантазируете.
Что фантазирую? Это вроде ты предлагаешь в код вставлять top(10) и тестом "фиксировать".

P>>>И тут мы узнаем... что тестами ничо не сделаешь. Гы-гы.

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

P>Как напишете ваш тест — кидайте сюда, а я покажу, какая фукнция проходит ваш тест и не удовлетворяет условию

P>Для простоты, функция будет такой — () -> true, т.к. никаких параметров и тд и тд и тд
P>Как наиграетесь, приходите
Не понял. Да, эта функция возвращает true. Тестом будет "f=() -> true; assertTrue(f())".

P>>>Соотвественно, гарантии у вас будут не с т.з. т.Райса, а с т.з. вероятностей

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


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

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

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

P>Хрупкий — это я вам сразу сказал. Так я ж и не предлагаю посимвольное сравнение на все случаи жизни.
deep.eq по-другому тупо не умеет.

P>·>И что ты этим хочешь сказать?

P>Да то, что искать тестами проблемные комбинации вы вспотеете. Нужно искать решение вне тестов.
Ясен пень, тестами комбинации не ищутся, а тестируются. Анализируешь код, coverage report, логи, спеки и т.п., потом пишешь тест подозрительной комбинации и смотришь как себя система ведёт.

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

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

P>Ну или чат-гпт спросите.



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

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

P>>>Вы совсем недавно собирались все комбинации тестировать, разве нет?

P>·>Не все e2e комбинации, а комбинации конкретно данного куска кода, sut.
P>С фильтрами комбинаций столько, что солнце погаснет раньше ваших тестов.
Даже если и так, то до e2e-комбинаций и жизни Вселенной не хватит уж точно.

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

P>>>Фильтры дают на десяток порядков больше комбинаций. В этом и проблем
P>·>Ну сделай 60 записей, будет как раз на десяток порядков больше комбинаций. В чём проблема-то?
P>Проблема в том, что бы генерить данные, нужно знать комбинации которые будут проблемными.
Это же требуется и для того чтобы чтобы руками проверять запросы.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[89]: Что такое Dependency Rejection
От: Sinclair Россия https://github.com/evilguest/
Дата: 07.03.24 07:04
Оценка: 18 (1)
Здравствуйте, ·, Вы писали:
P>>·>Только если у тебя какой-то очень частый случай и ты завязываешься на какую-то конкретную реализацию — а это значит, что у тебя тесты не будут тестировать даже регрессию.
P>>Покажите пример регрессии, которая не будет обнаружена этим тестом.
·>Ну загрузится пол таблицы. Или банально твой .top(10) не появится для какой-то конкретной комбинации фильтров, т.к. для этой комбинации решили немного переписать кусочек кода и забыли засунуть .top(10).
Нет. Эта проблема возникает только в том случае, если тестируется весь кусок про генерацию запроса целиком.
Вы предлагаете тестировать этот момент косвенно, через подсчёт реального количества записей, которые вернёт ваш репозиторий, подключенный к моку базы.
Альтернатива — не в том, чтобы сгенерировать 2N комбинаций условий фильтрации, а в том, чтобы распилить функцию построения запроса на две части:
1. Сгенерировать предикатную часть запроса
2. Добавить к любому запросу .top(10).

Тестируем эти две штуки по частям, и всё:
IQueryable<User> buildQuery(UserFilter criteria, int pageSize)
    => buildWhere(criteria).addLimit(pageSize);

Вот этот addLimit мы тестируем отдельным набором тестов, чтобы посмотреть, что он будет делать для отрицательных, нулевых, и положительных значений аргумента. Трёх бесконечно дешёвых юнит-тестов нам хватит для того, чтобы убедиться в его корректной работе с произвольным запросом. Так мы убираем множитель 3 из формулы 3*2N.
Шанс "забыть засунуть .top(10)" у нас ровно в одном месте — конкретно вот в этой glue-функции, которая клеит два куска запроса.
Для того, чтобы этот шанс окончательно устранить, нам достаточно 1 (одного) теста для функции buildQuery — благодаря линейности кода, этот тест покроет нужный нам путь.
А 2^N комбинаций фильтров уезжают в функцию buildWhere, которая тестируется отдельно.
При её тестировании мы точно так же заменяем 2N тестов на 2+2N-1 — режем на части примерно таким образом:
IQueryable<User> buildWhere(UserFilter criteria)
{
  var q = db.Users;
  if (criteria.Name != null)
    q = q.Where(u=>u.Name.Contains(criteria.Name));
  ...
  return q;
}

теперь нам достаточно двух тестов — в одном criteria.Name == null, в другом — не-null. Test Coverage нам покажет, что все строчки покрыты, а сам тест убедится, что у нас там — ровно нужный предикат — Сontains, BeginsWith, EndsWith или что там у нас требовалось по ТЗ.
Со следующим критерием всё будет точно так же — мы расщепляем код на линейно независимые куски, каждый из которых тестируется по отдельности. Если есть боязнь того, что нубас напишет некорректную комбинацию if-ов, вроде такой:
  var q = db.Users;
  if (criteria.Name != null)
    q = q.Where(u => u.Name.Contains(criteria.Name))
  else
  if (criteria.MinLastLoginTimestamp.HasValue)
     q = q.Where(u => u.LastLoginTimeStamp >= criteria.MinLastLoginTimestamp.Value);
       
  ...

То пилим функцию на несколько кусков:
IQueryable<User> buildWhere(UserFilter criteria)
  => db.Users
    .FilterName(criteria.Name)
    .FilterLastLogin(criteria.MinLastLoginTimeStamp, criteria.MaxLastLoginTimeStamp)
    .FilterDomain(criteria.Domain)
    .FilterGroupMembership(criteria.Groups)

Эта функция — строго линейна, она проверяется ровно одним тестом, который убеждается, что мы не забыли собрать все компоненты предиката.
А каждая из запчастей проверяется двумя или четырьмя тестами для всех нужных нам комбинаций её значений.
Всё, мы вместо 144 тестов (3*24*3) получили 3+2+4+2+2+3+1 = 17 тестов. При этом нам не нужны ни моки БД, ни тестовая БД в памяти, в которую запихано большое количество записей с разными комбинациями параметров.
То есть мы имеем 17 мгновенных тестов вместо 144 медленных, и гарантию полного покрытия.
По мере роста количества возможных критериев, разница между O(N) и O(2N) будет ещё больше.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[89]: Что такое Dependency Rejection
От: Pauel Беларусь http://blogs.rsdn.org/ikemefula
Дата: 07.03.24 10:53
Оценка:
Здравствуйте, ·, Вы писали:

P>>моки и конформационные тесты это не решение, а компенсация.

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

Имено — за счет покрытия.

P>>Вы отказываетесь идти дальше, и делать внятное покрытие e2e, и посмеиваетесь с тех, кто всё таки покрывает эти 99% как следует.

·>Скорее завидую простоте системы, что её реально покрыть e2e на 99%.

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

P>>Пример у вас есть внятный? Мне непонятно, каким чудом моки репозитория помогут понять, что же с интеграцией не так.

·>Они не для этого.

В том то и дело — вы оставили себе только полуинтеграционные тесты на моках.
Юнит-тесты вы не пишете, т.к. "это детали реализации, их не надо писать"
e2e "слишком долго"

P>>Какой компоент системы вы собираетесь конф тестами покрывать?

·>Конф-тесты тестируют чужое api. Система (по крайней мере её прод-часть) не принимает участие в конф-тестах.

Про то и речь — про интеграцию ваших компонентов с другими вашими же компонентами они ничего не говорят.

P>>Расскажите внятно, как мок репозитория поможет вам с интеграцией уровня приложения?

·>Никак. Мок поможет тестировать часть приложения с меньшим количеством ресурсов, следовательно позволит иметь больше покрытых сценариев.

В сценариях нас интересует интеграция прежде всего.
Например — юзер регистрируется, уточняет сведения, подтверждает регистрацию, делает N действий, смотрит в аудит, все ли хорошо, и делает логаут.
Итого:
Данные от входного запроса POST /users будут передаваться как положено во все части системы, и обратно, и это будет отражено везде — в профайле юзера, во всех частях админке
А далее, апдейт того же юзера снова даст ожидаемый результат и в профайле, и во всех частях админки
И так со всеми действиями согласно сценарию до самого последнего.

Если вы моками нарезали весь этот путь на части, вы знаете, что внутри каждой из частей всё хорошо
А вот склейку всех частей тестируют тесты системного уровня, например, те самые e2e

P>>Наоборот, e2e показывает те ошибки, которые проходят мимо ваших моков.

·>Если оно это показывает случайно, через несколько часов — то это называется хрень, а не показания.

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

P>> Они показывают, что приложение склеено как положено.

·>Это и проблема твоего подхода — у тебя в приложении на строчку кода ведро клея, который никак и не тестируется, кроме как тонной e2e.

Обратите внимание — сколько бы я вам ни говорил про юнит-тесты и интеграционные, вы всё равно видите своё.
Я даже цифры привел
Еще раз

e2e — меньше всего, десятки
интеграционные, функциональные — x10..x100 раз больше чем e2e
юнит-тесты, компонентные — x10..x100 раз больше, чем интеграционных

Вы выбросили:
верхний уровень — долго,
нижний — это "детали реализации"
Сверх этого кастрировали средний уровень и получили полуинтеграционные на моках

Итого — от вашей пирамиды остался поясок стыдливости

P>>Ну да — вы забили на интеграционные тесты.

·>Ну так в моём подходе этой интеграции кот наплакал, поэтому "приложение не упало при старте" уже практически означает, что 100% тех ошибок, "которые проходят мимо ваших моков" показано.

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

P>>·>Не годится. Мы не можем релизиться только с тремя десятками сценариев. Надо проверять 100%.

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

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

P>>Вы уже релизитесь без 99%. Если к вашим тестами добавите e2e, то покрытие станет плотнее.

·>Не станет.

e2e регулярно находят проблемы которые другими тестам даже обнаружить сложно.

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

·>Зачем? Если они что-то обнаружат, это значит что у клиентов проблемы прямо сейчас, prod incident. Поздно борожоми пить.

Затем, что бы
1 обнаружить на самых ранних этапах
2 сразу собрать максимальное количество сведений. По этим тестам у вас больше всего сведений, как и что воспроизвестир
3 e2e репортает все найденные проблемы

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

Итого — разница 3:0 в пользу e2e

P>>Вы уже это делаете. e2e всего то сделают видимыми проблемы в тех 99% которые ходили мимо ваших моков.

·>Ещё раз. Если ты догадался нечто описать в виде теста, пусть e2e — это значит какой-то вполне определённый известный сценарий. Всегда есть возможность покрыть этот сценарий покрыть более дешёвыми тестами и выполнять до релиза, всегда, а не по броску монеты.

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

P>>Это способ выполнять те тесты, которые у вас вообще отсутствуют.

·>Потому что нахрен не нужны.

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

P>>Вы тестируете минимально, репозиторий + бд. Эта часть есть и у меня.

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

Там же где и у вас — это часть интеграционных тестов.

P>>Комбинации различных условий в фильтре, что дает нам ту самую data complexity, и проблемные комбинации вы будете узнавать только на проде, одну за другой

·>Ты тоже.

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

·>Причём тут текущее время? Проблема как раз была в том, что ты называешь "нефункциональные требования": "ну юзер же создался, а что ещё не совсем до конца создался, ничего страшного..".


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

P>>Вот-вот. И если BA даёт добро — всё хорошо.

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

Разумеется.

P>>Уже в который раз уточняю, но вы всё равно видите чтото свое

·>В прыжке переобуваешься. Это совершенно не то, что ты изначально утверждал "БЛ начала вызываться через очередь, с задержкой от часов до дней". Написал "БЛ", а на самом деле телепатировал, есть части логики нужные сейчас, а есть части не сейчас. Если ты не понимаешь как это выражается с моками, задавай вопросы, а не фантазируй проблемы.

Что вас смущает? БЛ вызывается из разных мест приложения. Ключевое — консистентность тех или иных сведений.

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

P>>Если руками, что следует из выделеного, то какие варианты?
·>Мде, отстой.

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

P>>Я вам выделил, что бы виднее было.

·>Что за тест "против бд минимально заполненой под задачу"? Какие части системы он тестирует? e2e? или связка репо+бд? Или ещё что?

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

P>>Не работает — вы не знаете проблемной комбинации для фильтров. Знаете только что они есть и их хрензнаетсколькилион

·>Комбинация ровно та же — что и в твоём тесте. Тест почти такой же — только вместо деталей реализации ассертится поведение.

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

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

·>Эта проблема не исключается тестом, как бы тебе это ни хотелось.

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

P>>Покрывается. Я ж вам показал. Только решение и покрытие это разные вещи. В данном случае решение — постусловие. А тестами фиксируем, что бы не убежало

·>Что значит "не убежало"?

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

P>>Покажите пример регрессии, которая не будет обнаружена этим тестом.

·>Ну загрузится пол таблицы.

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

> Или банально твой .top(10) не появится для какой-то конкретной комбинации фильтров, т.к. для этой комбинации решили немного переписать кусочек кода и забыли засунуть .top(10).


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

P>>Необязательно. Если я руками пишу весь запрос — то будет в коде явно. но запрос может и билдер создать.

·>Магически что-ли? Билдер рандомно top(10) не вставит, будет некий кусок кода который его ставит.

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

P>>Некоторые детали нужно фиксировать в тестах, что бы случайно залетевший дятел не разрушил цивилизацию.

·>Дятлы тесты тоже умеют редактировать.

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

> А с учётом того, что у тебя в тестах полный текст sql — то это будет простой copy-paste и вечнозелёный тест. А на ревью определить корректность sql на вид могут не только лишь все.


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

P>>Решение — пост-условие. А вот тест который я показал, он всего то гарантирует сохранность этого пост-условия.

·>Не гарантирует.

Видите — снова без аргументов, голословно

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

·>Ты не понял. Я могу сделать это не руками, а кодом.

Для простых фильров это работает. Для сложных — проще найти решение напрямую, в бд, что бы представлять, куда всё двигать

P>>Есть и такие — их проще писать сразу, проще отдладить по горячим следам.

P>>Но с ростом сложности приходится вводить дополнительные инструменты.
·>Это где ты говорил такое месяц назад? Цитату.

Вам не только я, но и Синклер это же сказал.

P>>Пост-условия похоже для вас пустой звук, вам это всё равно кажется тестом. Тест всего лишь гарантирует сохранность пост-условия

·>Не гарантирует.

У вас похоже тесты вообще ничего не гарантируют. Зачем же вы их пишете?

P>>И решения и у меня, и у вас, для подобных кейсов будут одни и те же.

·>Ну может быть колонок id несколько разных и иногда они совпадают... но не всегда.

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

P>>Вы снова чего то фантазируете.

·>Что фантазирую? Это вроде ты предлагаешь в код вставлять top(10) и тестом "фиксировать".

Это же пост-условие.

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

·>"всегда" — это уже нетривиальное свойство. Проверка тривиального свойства это по сути "выполнить код и поглядеть на результат", т.е. тест практически.

Ловко вы опровергли теорему Райса!
В т.Райса нетривиальное свойство означает, что есть функции, которые им обладают, а есть те, которые не обладают.
То есть, искать разницу между двумя функциями вы будете до скончания времён

P>>Как наиграетесь, приходите

·>Не понял. Да, эта функция возвращает true. Тестом будет "f=() -> true; assertTrue(f())".

Вот-вот. Опровержение теоремы Райса! Ну Нобелевка же!

P>>Смотрите выше, текст выделен H1, как раз эту вашу способность и "демонстрирует"

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

Ручное тестирование плохо не само по себе, а когда оно увеличивает время разработки
В данном случае мы ищем решение — потому и тестируем руками, т.к. запрос ни разу не тривиальный и нужно быстро перебрать десяток другой вариантов.
Это намного быстрее чем делать то же самое кодом к orm или билдером запросов.
В тривиальный случаях, типа "user.byId()" ровно наоборот — быстрее именно кодом

P>>Хрупкий — это я вам сразу сказал.

Так я ж и не предлагаю посимвольное сравнение на все случаи жизни.

·>deep.eq по-другому тупо не умеет.

Я снова выделил, что бы вам виднее было. Посимвольно это потому, что у меня в конкретном случае нет ни json, ни orm, ни еще какого ast.
deep.eq дает нам вполне годную структурную эквивалентность.
Если вы ей не умеете пользоваться — значит это не ваш вариант

P>>Да то, что искать тестами проблемные комбинации вы вспотеете. Нужно искать решение вне тестов.

·>Ясен пень, тестами комбинации не ищутся, а тестируются. Анализируешь код, coverage report, логи, спеки и т.п., потом пишешь тест подозрительной комбинации и смотришь как себя система ведёт.

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

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

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

Жалко, что нет шрифта больше чем h1

сначала руками, а потом тестом против бд минимально заполненой под задачу


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

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

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

P>>С фильтрами комбинаций столько, что солнце погаснет раньше ваших тестов.

·>Даже если и так, то до e2e-комбинаций и жизни Вселенной не хватит уж точно.

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

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

·>Это же требуется и для того чтобы чтобы руками проверять запросы.

Похоже, пост-условия для вас пустой звук. Для моего варианта нужно проверить,
1 работает ли пост-условие
2 находится ли оно на своем месте
3 все ли ветки ифов получат его
Вот это — решение. А тестами фиксируем сохранность этого условия. Т.е. тесты это второстепенная роль в данном случае.
Отредактировано 08.03.2024 8:33 Pauel . Предыдущая версия .
Re[90]: Что такое Dependency Rejection
От: · Великобритания  
Дата: 07.03.24 12:00
Оценка: -1 :)
Здравствуйте, Sinclair, Вы писали:

P>>>·>Только если у тебя какой-то очень частый случай и ты завязываешься на какую-то конкретную реализацию — а это значит, что у тебя тесты не будут тестировать даже регрессию.

P>>>Покажите пример регрессии, которая не будет обнаружена этим тестом.
S>·>Ну загрузится пол таблицы. Или банально твой .top(10) не появится для какой-то конкретной комбинации фильтров, т.к. для этой комбинации решили немного переписать кусочек кода и забыли засунуть .top(10).
S>Нет. Эта проблема возникает только в том случае, если тестируется весь кусок про генерацию запроса целиком.
Мой поинт, что такой тест запроса целиком должен быть в любом случае. Что делает ценность этих ваших тестов <=0.

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

Я предлагаю тестировать не моменты, а бизнес-требования. Не бывает в реальности таких требований как "построить кусочек запроса".

S>Альтернатива — не в том, чтобы сгенерировать 2N комбинаций условий фильтрации, а в том, чтобы распилить функцию построения запроса на две части:

S>1. Сгенерировать предикатную часть запроса
Это всё скучные тривиальные детали реализации.

S>2. Добавить к любому запросу .top(10).

Вот только невозможно проверить в _тесте_, что .top(10) действительно добавляется к _любому_ запросу.

S>Вот этот addLimit мы тестируем отдельным набором тестов, чтобы посмотреть, что он будет делать для отрицательных, нулевых, и положительных значений аргумента.

Это уже лажа. addLimit это метод некоего QueryBuilder, некий библиотечный метод, который уже имеет свои тесты, включая эти твои отрицательные аргументы. Мы полагаемся, что он работает в соотвествии с контрактом когда им пользуемся. А здесь мы говорим о тестировании некего условного UserRepository, которому какие-либо QueryBuilders совершенно побоку. В контексте разговора UserRepositoryTest предлагать тесты работоспособности addLimit — это как минимум паранойя.

S>Шанс "забыть засунуть .top(10)" у нас ровно в одном месте — конкретно вот в этой glue-функции, которая клеит два куска запроса.

S>Для того, чтобы этот шанс окончательно устранить, нам достаточно 1 (одного) теста для функции buildQuery — благодаря линейности кода, этот тест покроет нужный нам путь.
А зачем этот тест нужен-то? Убедиться, что в двух строчках кода одна из них действительно addLimit? Это и так напрямую явно видно в коде и каждый заметит наличие-отсутствие этого вызова и в PR диффе.
Ты по сути предлагаешь написать строчку кода, а потом продублировать в тесте, что эта строчка действительно написана. Ценность околонулевая. Твои тесты тестируют "works as coded".
Завтра придёт "специалист" и немного допишет:
IQueryable<User> buildQuery(UserFilter criteria, int pageSize)
    => someMagicalCondition(criteria) ? buildOptimizedQuery(criteria) : buildWhere(criteria).addLimit(pageSize);

И твои супер-тесты это не обнаружат. Т.е. твой тест даст false negative. Или тебе известна методология тестирования кода на простоту и линейность?

Мой поинт в том, что ценным тестом будет проверка что addLimit строчка действительно работает в соответствии с бизнес-требованиями — запустить на 11 записях и заассертить, что вернулось только 10 как прописано в FRD — это цель этой строчки, а сама по себе строчка — это средство. И заметь, такой 11/10 тест достаточно сделать для ровно той же комбинации фильтров что и в твоём тесте на наличие addLimit в коде, никакой экспоненциальности которой ты грозишься. Твой тест теоретически не может гарантировать, что некая функция buildQuery возвращает IQueryable с addLimit всегда, для любых входных параметров, такое можно лишь гарантировать через ревью текста самого кода.
Цель моего теста: если мы завтра перепишем addLimit на голый sql c "where rownum <= 10" или наоборот — то у нас будет регрессия, т.е. тесты которые так же продолжают работать и ожидаются, что должны быть зелёными без каких-либо изменений после рефакторинга. Такие тесты нужны в любом случае. А твой тест даст false positive и будет просто выкинут. Напишут взамен другой тест, в который закопипастят .contains("rownum < 10") перепутав что rownum zero- или one- based. Отличить на ревью корректность <= от < гораздо сложнее, чем условно save(...11 records...); assert find(10).size() == 10; assert find(5).size() == 5;.

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

Это какой-то тривиальный случай. Это значит не то, что мы магически сделали функцию линейной, а что никаких зависимостей между фильтрами нет в требованиях и никаких 2^N комбинаций просто не нужно, где тут эта ваша грозная data complexity — совершенно неясно.
В реальности у тебя могут быть хитрые зависимости между параметрами фильтра и вот тут и полезут неявные комбинации экспоненциально всё взрывающие и код несводимый к линейной функции, а будет хитрая if-else-switch лесенка.

S>А каждая из запчастей проверяется двумя или четырьмя тестами для всех нужных нам комбинаций её значений.

S>Всё, мы вместо 144 тестов (3*24*3) получили 3+2+4+2+2+3+1 = 17 тестов. При этом нам не нужны ни моки БД, ни тестовая БД в памяти, в которую запихано большое количество записей с разными комбинациями параметров.
S>То есть мы имеем 17 мгновенных тестов вместо 144 медленных, и гарантию полного покрытия.
Гарантию того, что у тебя works as coded. Зато отличить u => u.LastLoginTimeStamp >= criteria.MinLastLoginTimestamp.Value) от u => u.LastLoginTimeStamp <= criteria.MinLastLoginTimestamp.Value) такие тесты не смогут, т.к. пишутся как правило копипастом. На ревью очепятку очень вряд ли кто-то заметит.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 07.03.2024 13:40 · . Предыдущая версия . Еще …
Отредактировано 07.03.2024 12:03 · . Предыдущая версия .
Re[91]: Что такое Dependency Rejection
От: Sinclair Россия https://github.com/evilguest/
Дата: 07.03.24 14:21
Оценка:
Здравствуйте, ·, Вы писали:

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

·>Я предлагаю тестировать не моменты, а бизнес-требования. Не бывает в реальности таких требований как "построить кусочек запроса".
Это игра словами.

·>Это всё скучные тривиальные детали реализации.

Ну так дьявол-то как раз в деталях.

·>Вот только невозможно проверить в _тесте_, что .top(10) действительно добавляется к _любому_ запросу.

Я же показал, как именно проверить. Какое место осталось непонятным?

·>Это уже лажа. addLimit это метод некоего QueryBuilder, некий библиотечный метод, который уже имеет свои тесты, включая эти твои отрицательные аргументы. Мы полагаемся, что он работает в соотвествии с контрактом когда им пользуемся.

Если AddLimit — библиотечный, то всё прекрасно, и вы правы — тестировать его не надо вовсе.

·>А зачем этот тест нужен-то? Убедиться, что в двух строчках кода одна из них действительно addLimit? Это и так напрямую явно видно в коде и каждый заметит наличие-отсутствие этого вызова и в PR диффе.

Ну вы же почему-то не верите в то, что в двух строчках кода в контроллере берётся именно request.ClientTime и требуете, чтобы это было покрыто тестами

·>
·>IQueryable<User> buildQuery(UserFilter criteria, int pageSize)
·>    => someMagicalCondition(criteria) ? buildOptimizedQuery(criteria) : buildWhere(criteria).addLimit(pageSize);
·>

·>И твои супер-тесты это не обнаружат.
У меня не супер-тесты, а самые примитивные юнит-тесты.
·>Т.е. твой тест даст false negative. Или тебе известна методология тестирования кода на простоту и линейность?
Основная методология — такая же, как вы привели выше. Каждый заметит наличие-отсутствие этого вызова и в PR диффе, поэтому такой код не пройдёт ревью. От автора потребуют переместить свой buildOptimizedQuery внутрь buildWhere. Если у нас нет под рукой theorem prover, то надёжных автоматических альтернатив нет.
Впрочем, в вашем подходе всё будет ровно так же — вы же ведь не собираетесь перебирать все мыслимые сочетания значений аргументов в тесте, который проверяет количество записей?

·>Мой поинт в том, что ценным тестом будет проверка что addLimit строчка действительно работает в соответствии с бизнес-требованиями — запустить на 11 записях и заассертить, что вернулось только 10 как прописано в FRD — это цель этой строчки, а сама по себе строчка — это средство. И заметь, такой 11/10 тест достаточно сделать для ровно той же комбинации фильтров что и в твоём тесте на наличие addLimit в коде, никакой экспоненциальности которой ты грозишься.

Ну это же просто обман. Смотрите за руками: если комбинация фильтров, которая использовалась в тесте на addLimit, попадает под условия someMagicalCondition, то тест сразу сфейлится.
А если не попадает — то ващ тест точно так же вернёт 10 записей, и всё.

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

·>Цель моего теста: если мы завтра перепишем addLimit на голый sql c "where rownum <= 10" или наоборот — то у нас будет регрессия,
Всё правильно. Вредителей, которые заменяют рабочий код на голый SQL, нужно бить по рукам.
·>т.е. тесты которые так же продолжают работать и ожидаются, что должны быть зелёными без каких-либо изменений после рефакторинга. Такие тесты нужны в любом случае. А твой тест даст false positive и будет просто выкинут. ·>Напишут взамен другой тест, в который закопипастят .contains("rownum < 10") перепутав что rownum zero- или one- based. Отличить на ревью корректность <= от < гораздо сложнее, чем условно save(...11 records...); assert find(10).size() == 10; assert find(5).size() == 5;.
Если в проекте завёлся вредитель, то никакими тестами вы его не поймаете. Он просто возьмёт и сломает всё, что угодно. Сейчас вы мне рассказываете, что ловите баги при помощи review кода.
Ну, вот и в нашем случае делается review кода, только код — простой, а за наращивание сложности на ровном месте можно бить человека палкой.

·>Это какой-то тривиальный случай. Это значит не то, что мы магически сделали функцию линейной, а что никаких зависимостей между фильтрами нет в требованиях и никаких 2^N комбинаций просто не нужно, где тут эта ваша грозная data complexity — совершенно неясно.

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

·>В реальности у тебя могут быть хитрые зависимости между параметрами фильтра и вот тут и полезут неявные комбинации экспоненциально всё взрывающие и код несводимый к линейной функции, а будет хитрая if-else-switch лесенка.

Нет, по-прежнему будет простая линейная цепочка, только кусочки её будут чуть более длинными.

·>Гарантию того, что у тебя works as coded. Зато отличить u => u.LastLoginTimeStamp >= criteria.MinLastLoginTimestamp.Value) от u => u.LastLoginTimeStamp <= criteria.MinLastLoginTimestamp.Value) такие тесты не смогут, т.к. пишутся как правило копипастом. На ревью очепятку очень вряд ли кто-то заметит.

Если пишут разные люди, то заметят. Ну и, опять же, у нас код каждой функции — очень короткий. Там всего-то пара операций.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[90]: Что такое Dependency Rejection
От: · Великобритания  
Дата: 08.03.24 12:46
Оценка:
Здравствуйте, Pauel, Вы писали:

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

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

P>>>Вы отказываетесь идти дальше, и делать внятное покрытие e2e, и посмеиваетесь с тех, кто всё таки покрывает эти 99% как следует.

P>·>Скорее завидую простоте системы, что её реально покрыть e2e на 99%.
P>Вы сами себя ограбили когда строили вашу пирамиду тестов в виде трапеции — полуинтеграционные на моках + конформанс, которые к вашей системе имеют слабое отношение.
В чём ограбили? На прогон тестов и релиз мы тратим как лохи всего пол часа и уныло идём домой, скучаем на прод-саппорте, когда за углом тесты гоняют неделями и нон-стоп вечеринка в проде с юзерами?

P>>>Пример у вас есть внятный? Мне непонятно, каким чудом моки репозитория помогут понять, что же с интеграцией не так.

P>·>Они не для этого.
P>В том то и дело — вы оставили себе только полуинтеграционные тесты на моках.
P>Юнит-тесты вы не пишете, т.к. "это детали реализации, их не надо писать"
Юнит-тесты с моками — это тоже юнит-тесты. Ты до сих пор не въехал что такое моки и как ими надо пользоаться.

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

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

P>·>Конф-тесты тестируют чужое api. Система (по крайней мере её прод-часть) не принимает участие в конф-тестах.

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

P> И так со всеми действиями согласно сценарию до самого последнего.

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

P>Если вы моками нарезали весь этот путь на части, вы знаете, что внутри каждой из частей всё хорошо

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

P>>>Наоборот, e2e показывает те ошибки, которые проходят мимо ваших моков.

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

P>>> Они показывают, что приложение склеено как положено.

P>·>Это и проблема твоего подхода — у тебя в приложении на строчку кода ведро клея, который никак и не тестируется, кроме как тонной e2e.
P>Обратите внимание — сколько бы я вам ни говорил про юнит-тесты и интеграционные, вы всё равно видите своё.
Потому что ты жонглируешь терминологией в соседних предожениях.

P>Вы выбросили:

Ты сочиняешь. Не выбросили. Пирамида такая и есть по форме.
Моки этому всему ортогональны. Они могут быть на любом уровне. Ещё раз, это тупо способ протаскивать данные, альтернатива параметрам.

P>Итого — от вашей пирамиды остался поясок стыдливости

Бредишь.

P>>>Ну да — вы забили на интеграционные тесты.

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

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

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

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

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

P>>>·>Не годится. Мы не можем релизиться только с тремя десятками сценариев. Надо проверять 100%.

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

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

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

P>>>Вы уже релизитесь без 99%. Если к вашим тестами добавите e2e, то покрытие станет плотнее.

P>·>Не станет.
P>e2e регулярно находят проблемы которые другими тестам даже обнаружить сложно.
Это потому что у вас беда с другими тестами. У нас в точности наоборот, вся та небольшая кучка e2e в подавляющем большинстве случаев просто работает на стейдже. А тот практически единственный TiL тест в худшем случае обнаруживет железные проблемы в инфре.

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

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

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

Мы собираем сведения ещё до планирования деплоя.

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

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

P>·>Ещё раз. Если ты догадался нечто описать в виде теста, пусть e2e — это значит какой-то вполне определённый известный сценарий. Всегда есть возможность покрыть этот сценарий покрыть более дешёвыми тестами и выполнять до релиза, всегда, а не по броску монеты.

P>Сценарии и так покрыты дешовыми тестами. Эти дешовые тесты, особенно на моках, не покрывают всю интеграцию, что очевидно.
Все условно полторы строчки интеграции? Какой ужас!

P>>>Это способ выполнять те тесты, которые у вас вообще отсутствуют.

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

P>>>Вы тестируете минимально, репозиторий + бд. Эта часть есть и у меня.

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

P>>>Комбинации различных условий в фильтре, что дает нам ту самую data complexity, и проблемные комбинации вы будете узнавать только на проде, одну за другой

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

P>Соответсвенно вариантов решения у меня несколько

P>Даже если я не знаю комбинации, то могу подстараховаться пост-условием
P>А что бы пост-условие не потерялось, подкидываю тест
Ещё молитву скажи и свечку поставь, тоже, говорят, помогает.

P>·>Причём тут текущее время? Проблема как раз была в том, что ты называешь "нефункциональные требования": "ну юзер же создался, а что ещё не совсем до конца создался, ничего страшного..".

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

P>>>Вот-вот. И если BA даёт добро — всё хорошо.

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

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

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

P>>>Я вам выделил, что бы виднее было.

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

P>>>Не работает — вы не знаете проблемной комбинации для фильтров. Знаете только что они есть и их хрензнаетсколькилион

P>·>Комбинация ровно та же — что и в твоём тесте. Тест почти такой же — только вместо деталей реализации ассертится поведение.
P>Что вы будете ассертить в поведении?
Что возвращается ровно столько записей сколько требуется.

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

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

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


P>>>Покрывается. Я ж вам показал. Только решение и покрытие это разные вещи. В данном случае решение — постусловие. А тестами фиксируем, что бы не убежало

P>·>Что значит "не убежало"?
P>Пришел некто резкий и дерзкий и фиксанул "тут всё просто, я быстро". На его тестовой бд записей 60 штук, мелочовочка. А на проде это десятки миллионов.
Твои тесты с паттернами дерзкого не остановят. Свечка и молитва — гораздо надёжнее твоих тестов! Рекомендую.

P>>>Покажите пример регрессии, которая не будет обнаружена этим тестом.

P>·>Ну загрузится пол таблицы.
P>Полтаблицы это 20 записей? Не понял, что за кейс такой.
У тебя же таблица это "это десятки миллионов".

>> Или банально твой .top(10) не появится для какой-то конкретной комбинации фильтров, т.к. для этой комбинации решили немного переписать кусочек кода и забыли засунуть .top(10).

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

P>>>Необязательно. Если я руками пишу весь запрос — то будет в коде явно. но запрос может и билдер создать.

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

P>>>Некоторые детали нужно фиксировать в тестах, что бы случайно залетевший дятел не разрушил цивилизацию.

P>·>Дятлы тесты тоже умеют редактировать.
P>Умеют — здесь только код-ревью может помочь. О чем я вам и говорю в который раз.
Так я об этом и говорю, что тесты в твоём виде бесполезны и даже вредны, т.к. код-ревью усложняют. Давай приведи пример целиком — код + твой тест с паттерном и я тебе тыкну пальчиком что на ревью может пойти не так. Это происходит потому, что код в тесте будет практически копипаст главного кода, значит ревьюверу вообще смысла на код теста смотреть нет, т.к. там тупо то же самое.

>> А с учётом того, что у тебя в тестах полный текст sql — то это будет простой copy-paste и вечнозелёный тест. А на ревью определить корректность sql на вид могут не только лишь все.

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

P>>>Решение — пост-условие. А вот тест который я показал, он всего то гарантирует сохранность этого пост-условия.

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

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

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

P>·>Это где ты говорил такое месяц назад? Цитату.

P>Вам не только я, но и Синклер это же сказал.
Цитату месячной давности в студию. Или не было.

P>>>Пост-условия похоже для вас пустой звук, вам это всё равно кажется тестом. Тест всего лишь гарантирует сохранность пост-условия

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

P>>>И решения и у меня, и у вас, для подобных кейсов будут одни и те же.

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

P>>>Вы снова чего то фантазируете.

P>·>Что фантазирую? Это вроде ты предлагаешь в код вставлять top(10) и тестом "фиксировать".
P>Это же пост-условие.
Я имел в виду, что если реквест потенциально выдаёт слишком много записей, то тупо брать только первые 10 — это очень странное решение. По уму надо либо делать пейджинг, либо просто слать отказ в виде 4xx/5xx кода, но нужно дать понять клиенту, что в его запросе неожиданно много записей, а не просто 10. Так что такое постусловие это какой-то очень частный случай который, честно говоря, я нигде не видел, чтобы это считалось нормой молча обрезать данные.

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

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

P>·>Не понял. Да, эта функция возвращает true. Тестом будет "f=() -> true; assertTrue(f())".

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

P>>>Смотрите выше, текст выделен H1, как раз эту вашу способность и "демонстрирует"

P>·>Так ещё до того как ты это написал уже говорил, что _необходимость_ ручного тестирования — проблема. Не надо так делать и рассказал как избежать эту необходимость.
P>Ручное тестирование плохо не само по себе, а когда оно увеличивает время разработки
P>А когда сокращает — очень даже хорошо
Это сигнал, что автоматическое тестирование организовано плохо.

P>>>Хрупкий — это я вам сразу сказал.

P>

Так я ж и не предлагаю посимвольное сравнение на все случаи жизни.

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

P>deep.eq дает нам вполне годную структурную эквивалентность.

P>Если вы ей не умеете пользоваться — значит это не ваш вариант
Мы умеем ей не пользоваться.

P>>>Да то, что искать тестами проблемные комбинации вы вспотеете. Нужно искать решение вне тестов.

P>·>Ясен пень, тестами комбинации не ищутся, а тестируются. Анализируешь код, coverage report, логи, спеки и т.п., потом пишешь тест подозрительной комбинации и смотришь как себя система ведёт.
P>И так будете для каждой комбинации, да? Дадите шанс юзеру убиться об ваш билдер фильтров?
Зачем для каждой? Классифицируем, моделируем, алгоритмизируем, етс. Вон как у Sinclair оказалось, что функция-то линейная и никаких экспоненциальых переборов комбинаций и не требуется, покрывается полностью от силы десятком тестов.

P>·>А этот кто-то как будет, ногами что-ли?

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

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

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

P>Мне что, каждую строчку теперь H1 выделять, что бы вам виднее было?

Нет, достаточно внятно излагать свои мысли и отвечать на вопросы которые я задаю.

P>>>С фильтрами комбинаций столько, что солнце погаснет раньше ваших тестов.

P>·>Даже если и так, то до e2e-комбинаций и жизни Вселенной не хватит уж точно.
P>В том то и дело — количество e2e никак не зависит от количества комбинаций в фильтрах.
Почему?

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

P>·>Это же требуется и для того чтобы чтобы руками проверять запросы.
P>Похоже, пост-условия для вас пустой звук. Для моего варианта нужно проверить,
P>1 работает ли пост-условие
P>2 находится ли оно на своем месте
P>3 все ли ветки ифов получат его
Именно. И для этого нужно знать комбинации которые будут проблемными.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
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 . Предыдущая версия .
Re[92]: Что такое Dependency Rejection
От: · Великобритания  
Дата: 09.03.24 23:07
Оценка:
Здравствуйте, 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 всегда использует битовую операцию взятия младшего бита".
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[93]: Что такое Dependency Rejection
От: Pauel Беларусь http://blogs.rsdn.org/ikemefula
Дата: 10.03.24 15:15
Оценка:
Здравствуйте, ·, Вы писали:

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

·>smoke tests — как вишенка на торт, но в остальном — проверять две вещи: части работают, части собираются между собой нужным образом.

Что это за тесты, кто их выполняет?

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


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

·>Минорная?! Без тестов обеспечить качество практически невозможно.

Минорная — это значит, что есть вещи более важные
1. методология
2. требования
3. дизайн согласно методологии
4. реализация согласно дизайна
5. квалификация людей в команде

Если у вас нет хотя бы одного 1..4 никакие тесты вам не помогут, хоть обмажтесь.
А вот если есть всё выше, то можете подключать
6. контроль качества
При этом, тесты это всего лишь часть контроля качества

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

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

Похоже, ваш капасити переполнен три месяца назад. Вы здесь, извините, порете отсебятину.

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

·>Где ты предлагаешь проверять поля на null тогда?

Какие именно поля на null ? ui, dto, bl, конфиг, бд?

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

P>>Как без таких тестов сможете убедиться, что на реальной системе у вас действительно эти 30 минут а не месяцы и годы?
·>Ты и со своими тестами не сможешь убедиться. Ещё раз, это достигается другим способом. Строишь модель физического времени, например как int64 unix-epoch nanoseconds. А дальше в тестах тупо прибавляешь разное количество наносекунд к моку источника времени и смотришь, что логаут не|произошел. Тогда и конкретное количество времени неважно, хоть 30 минут, хоть 30 лет — тест будет выполяться мгновенно в любом случае. И нет никаких проблем прогонять этот тест каждый раз перед каждым релизом.

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

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

P>>Если этот шаг неважный, его в e2e выносить не нужно. А если это важно, то это проверять надо регулярно:

·>Ок... ну может логаут неважный. Но вот например "отослать statement в конце каждого месяца" — шаг ооочень важный. Тоже e2e тест сделаешь со sleep(1 month)?

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

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

·>Падение e2e-теста означает дыру в нижележащих тестах. Если ваши e2e-тесты падают, чаще чем рак на горе свистит, значит у вас внизу пирамиды дырявые тесты.

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

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

·>Я под интеграционным кодом подразумеваю который вызывает "те ошибки, которые проходят мимо ваших моков". Так вот мимо моков проходят ошибки в доле процента кода, который выполняется только при реальном старте приложения, код в том самом composition root.

Ну да, вы выдумали какое то своё определение интеграционного кода. А ошибки с построением запроса фильтров, они как, по вашему, где ходят?

·>У технологического кода другой бизнес-домен. Технологический код, который, скажем, реплицирует базу записей в несколько датацентров — оперирует не бизнес-сущностью лайка, а "запись данных", "датацентр" и т.п. А дальше уже идёт интеграция, когда мы в качестве записи берём лайк и получаем надёжное реплицированное хранилище лайков. У тебя же твой билдер билдит конкретный запрос сущности из бизнес-домена, но вместо ассертов бизнес-сущностей ты тестируешь технические детали.


Потому, что покрывать тестами нужно все критические свойства, а не ограничиваться только теми, что в спеке написаны.

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

·>Только тот код, что в composition root сложно покрывается тестами, т.к. там создаются и инжектятся реальные объекты и требует дорогого e2e тестирования. Остальное — неинтересно, ибо быстро, тривиально и надёжно тестируется с помощью моков.
·>Если вы не в сосотянии протестировать контроллер, роутер, юзекйс валидацию и сериализацию без e2e — у вас проблемы.

Успокойтесь, e2e это не для теста контролера. Наверное вы забыли, что я вам предлагал тестировать валидацию юнит-тестами?

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

·>Ты предложил прогонять не все тесты перед выкаткой в прод.

Я вам даже рассказал как именно — все ваши тесты вместе взятые пускаете как сейчас. Допустим их 1000 штук. Вот эту 1000 оставьте и не трогайте.
А вот далее вы добавляете новый вид тестов, которые будут запускаться регулярно после деплоя. Их штук 10, новых.
Итого — 1000 пускаете как раньше, + 10 новых регулярно.

P>>Откуда возьмутся проблемы у юзеров на проде?

·>Не все тесты выполнены.

Похоже, X + 1 у вас может быть меньше X. Забавно!

P>>Ваши имеющиеся тесты отработали — за счет чего регрессия произойдет?

·>Наши да, ваши — не все.

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

P>>Ну и идите. У вас варианты

P>>1 о новых проблемах узнать наутро
P>>2 через неделю от суппорта.
P>>Вам какой вариант ближе? Дайте угадаю "у нас до прода баги недоходят"
·>Те, для которых написаны тесты — ясен пень не доходят. Если какой-либо тест красный для данной версии системы — в прод она не попадёт.

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

P>>Это все так делают.

·>Откуда тогда у тебя берутся красные тесты после деплоя в прод?

1 нагрузка
2 количество и качество юзеров итд
3 новое состояние, которого на тестовом окружении никогда не было
4 инфраструктура
5 внешние системы
6 ошибки, которые были не видны на тестовом окружении, но стали видны на проде, например, эффект накопления
7 редкие ошибки — прод тупо работает на порядки дольше тестового окружения

P>>И никто, кроме лично вас не утверждает "на проде багов быть не может"

·>Ты меня заебал. Не пиши свои бредовые фантазии как мои цитаты. Я такого никогда нигде не утверждал.

Смотрите, вот ваши цитата выше:
"Те, для которых написаны тесты — ясен пень не доходят(до прода)" — в скобках это контекст обозначил

Есть бага. Ну написали вы тест, два, десять, сто. И пофиксили багу. Зеленые тесты.
Но бага это не про фикс, а про поведение. А теперь см п 1..7 выше, каждый из этих пунктов спокойно может воскресить вашу багу.
А вы утверждаете, что раз есть тест, то такой баги на проде точно нет

P>>Вы из своих функций вызываете другие свои функции? Вот вам и интеграционный код. Посмотрите, сколько у вас такого.

·>Непокрытого лёгкими тестами? Полторы строчки, всё верно.

Сюда нужно вписать data complexity. Хороший пример — те самые фильтры.

P>>·>Т.е. у тебя есть ровно те же "репозиторий + бд" тесты?

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

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

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

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

Как это будет с linq вам Синклер рядом показал. Сама задача в том, что фильтровать нужно по выражению от юзера, который а хрен знает что может понавыбирать.

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

·>Потому что попытка юзера выполнить функцию проваливается из-за ошибок в синхронизации.

Необязательно, перестаньте фантазировать.

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

·>Ещё раз, эта проблема ассертами в тестах не решается.

В том то и дело. А что у вас есть кроме тестов?

P>>А дальше нам нужно

P>>1. зафиксировать паттерн — проверяем посимвольно за отсутствием других инструментов
P>>2. покрыть это тестом
·>Верно. Но ты не догоняешь, что во 2м пункте можно просто ассертить число записей, с тем же успехом, но лучшим результатом.

Что бы ассертить число записей нужно знать комбинацию которой у вас точно нет. Можно реализовать ленивую подгрузку. Это вобщем тот же вариант, вид сбоку — юнит-тестом вы можете зафиксировать именно такую особенность структуры.

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

P>>2. паттерн который соответствует дизайну
P>>3. код ревью
P>>4. тесты, которые фиксируют сохранность 1 и 2
P>>Вы из 1..4 видите только тесты, а всё остальное для вас пустое место.
·>Не потому что пустое место, а потому что мы тут обсуждаем тесты и разные подходы к тестированию, а 1..3 — оффтопик. Но ты всё не успокоишься и продолжаешь словоблудить.

Никакой это не оффтопик. пп 1 2 и 3 говорят о том, что можно изменить дизайн, и ваши моки будут больше не нужны.

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

·>Поставлен не туда или поставлен условно.

Вот вам и стало понятно, что покрываем тестами — поставлен именно туда, куда надо. Только сначала дизайн решения. См сообщение Синклера

P>>Наоборот. Потерять пост-условие в коде всегда легче легкого.

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

Другой, которого вы так и не показали?

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

P>>Цитирую себя
P>>Вы из 1..4 видите только тесты, а всё остальное для вас пустое место.
·>Т.е. твои тесты — проверяют меньше, чем мои. Мои, помимо того, что кол-во записей ограничивается 10ю так же протестирует и валидность запросов, подстановку парамов и т.п.

Мои тесты фиксируют структуру решения. Ваши без конкретного перечня комбинаций ничего не гарантируют.

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

·>Ты о чём? Что за исключённые тесты?

Закоментили, удалили, исправили ожидания. Если вы видите, что тест на .top убрали, надо пойти и дать по шее.

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

·>Завидуешь, да? Ну вообще говоря acceptance гоняется на кластере, вот и укладывается в пол часа.

Полчаса это десятки минут. e2e это вобщем тот же acceptance. Если у вас есть такое, чего же вы здесь выступаете?

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

·>Ну можно придумать что, например, для пустого списка cгенерится пустой фильтр.

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

P>>А вот "выполнится ли инструкция X" — вот такое сводится к проблеме останова.
·>В тесте же конкретный сценарий — запускаем код, дождались завершения, проверяем, выполнилась ли инструкция X. Никакой проблемы останова.

Вы успешно опровергли теорему Райса

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

·>Так ты зачем-то синтаксис и проверяешь "есть ли в коде .top(10)".

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

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

·>Если есть зрелая система автоматических тестов, это значит, что можно делая exploratory testing (это видимо что ты тут имеешь в виду) описывать новые сценарии, пользуясь уже готовым test harness. Тут же запускать их, смотреть на поведение системы и, может быть, даже закоммитить результат как новый автотест.

exploratory testing это
1. построение нового кейса-сценария-итд — это человек в уме делает, здесь он ограничен только воображением
2. запись шагов используя готовый набор инструментов

п1 вы можете переложить разве что на AI. До недавних дней это делал исключительно человек.

·>Если я правильно понял проблему, то она не находится никакими тестами, хоть ручными, хоть ножными. Если только случайно.


Её можно исключить методами родственными знакомой вам статической типизации. Те самые предусловия и соответствующий дизайн.

P>>Там, где у автоматических тестов начинаются недостатки, у других методов будут преимущества.

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

Ручные методы это в т.ч. exploratory, и вообще исследование.

·>Главное пока умалчиваешь: тест как выглядит для этого кода? Ну где там deep.eq, pattern или что?


Вам примера Синклера недостаточно, вы хотите у меня тож самое спросить?
const request = create(
    User, 
    u => u.many(q => q.filter(f => f(filterExpression))
                      .orderBy(o => u('created'))
                  .top(10)
    )
);

expect(builder({ параметры })).to.deep.eq(request)


вот так мы можем проверить, добавляет ли билдер top или нет. Билдер будет примерно как у Синклера.

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

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

Забавно, вы вырезали часть цитаты, и не знаете, что было в той части, что вы же и выбросили? Ищите — я для вас синим подсветил

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


Пример у вас есть?

P>>Вам нужно знание о том, как устроены четные и нечетные — у одних младший бит 1, у других — младший бит 0

·>Отлично. Вот ты и выявил какие комбинации будут проблемными. Придумать входные парамы и ожидания с вариациями младшего бита. Т.е. и тестировать надо именно на комбинациях младшего бита.

Комбинации как раз не выявлены. Только свойство построения чисел

P>>В переводе на фильтры — у запроса всегда есть лимит.

·>"у запроса всегда есть лимит" в переводе в твоём случае будет "функция isOdd всегда использует битовую операцию взятия младшего бита".

Вы уже почти научились. В случае с битами все просто — половина целых четные, половина — нечетные. Это значит, что тестовый набор вы сможете намастырить самостоятельно.
А вот где взять запрос, у которого будет нарушено свойство длины — загадка
Re[94]: Что такое Dependency Rejection
От: · Великобритания  
Дата: 11.03.24 12:35
Оценка:
Здравствуйте, Pauel, Вы писали:

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

P>·>smoke tests — как вишенка на торт, но в остальном — проверять две вещи: части работают, части собираются между собой нужным образом.
P>Что это за тесты, кто их выполняет?
CI/CD

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

P>>>Причин может быть много. Качество не сводится к тестам. Более того — тесты в обеспечении качества это минорная часть.
P>·>Минорная?! Без тестов обеспечить качество практически невозможно.
P>Минорная — это значит, что есть вещи более важные
P>Если у вас нет хотя бы одного 1..4 никакие тесты вам не помогут, хоть обмажтесь.
А без тестов 1-6 уйдут лесом. Единственное, что может не сразу, но уйдут точно.

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

P>·>Связь в том, что ты в своих юнит-тестах предлагаешь проверять поля на null, но интеграцию этого же кода с реальной субд — провеять уже через e2e.
P>Похоже, ваш капасити переполнен три месяца назад. Вы здесь, извините, порете отсебятину.
У тебя память как у рыбки. Цитирую:

·>потенциально может быть null — ты не дождёшься окончания.
Это полные тесты, где всё тащится медленно и реально


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

P>·>Где ты предлагаешь проверять поля на null тогда?
P>Какие именно поля на null ? ui, dto, bl, конфиг, бд?
Перечитай топик, всё написано. Твоя цитата:

...в составе юнит-тестов, где нас интересуют подробности
— а что если в колонке пусто


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

P>·>Ты и со своими тестами не сможешь убедиться. Ещё раз, это достигается другим способом. Строишь модель физического времени, например как int64 unix-epoch nanoseconds. А дальше в тестах тупо прибавляешь разное количество наносекунд к моку источника времени и смотришь, что логаут не|произошел. Тогда и конкретное количество времени неважно, хоть 30 минут, хоть 30 лет — тест будет выполяться мгновенно в любом случае. И нет никаких проблем прогонять этот тест каждый раз перед каждым релизом.
P>Смотрите внимательно — щас будут примеры и вы наверняка скажете, что у вас такого быть не может, невозможно, никогда, у вас багов вообще не бывает итд.
Таких примитивных тривиальных багов как у вас — не бывает. Ибо это всё лечится элементарно.
Подавляющее большинство багов — неверно поняли спеку и реализовали поведение не такое, какое хотел юзер. Но такие баги в большинстве ловятся на этапе демо.

P>Итого:

P>Написали вы такой тест на моках, а на проде выяснилось, что юзер всё равно не логаутится. Например, пототому, что в UI приложении есть фоновый процессинг, который мимоходом и обновляет токен продляя сессию.
Так фоновой процессинг не с божьего попущения работает, а с того же мочёного источника времени. Т.е. тест будет выполнять и все шедулеры-фоновые процессы при перемотке времени. В этом и цель этих моков — полный контроль внешних зависимостей. С этим будут проблемы только у тебя, т.к. у тебя по всему коду глобальные переменные в виде DateTime.Now() и никак не контролируются.

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

Не понял. По ошибке указали в прод конфиге не 30 минут, а 300?
На это дело есть специальный процесс, который показывает diff конфига между продом и тестом. Каждая разница должна быть проревьювена и заапрувлена.

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

Детский сад. Не используйте глобальные переменные.

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

Забитость очереди — это красный сигнал в мониторинге.

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

"строчка тестового кода" это как?! Тестовый код физически лежит в другом каталоге от прода.

P>Проблему нужно решать не моками, а дизайном.

Ага, т.е. опять оффтоп. Мы же тут обсуждаем тесты, а не дизайн.

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

Мде. Тупо — не используйте глобальные переменные.

P>А если время сессии это критическая вещь, то e2e ну просится — архитектура аутентификации всегда сложная, там много чего может пойти странными путями.

Это легко лечится: Любая вещь, сложная или не сложная — первый вопрос на который должен быть дан ответ прежде чем приступать к обсуждению дизайна и уж тем более реализации — как мы это можем покрыть быстрыми автотестами?

P>>>Если этот шаг неважный, его в e2e выносить не нужно. А если это важно, то это проверять надо регулярно:

P>·>Ок... ну может логаут неважный. Но вот например "отослать statement в конце каждого месяца" — шаг ооочень важный. Тоже e2e тест сделаешь со sleep(1 month)?
P>В данном случае здесь решение что через мониторинг, что через e2e будет приблизительно одинаковым.
Мониторинг обнаруживает проблемы мгновенно, в отличие от.

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

e2e чего? e2e — это сценарий, когда какой-то тестовый юзер чего-то сделал и проверяем, что система совершила ожидаемые действия. А statement создаётся для реальных юзеров для действий которые они совершили.

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

P>·>Падение e2e-теста означает дыру в нижележащих тестах. Если ваши e2e-тесты падают, чаще чем рак на горе свистит, значит у вас внизу пирамиды дырявые тесты.
P>Нету способа гарантировать отсутствие дыр, багов, уязвимостей итд. e2e это способ задешево выявить большинство проблемав в интеграции верхнего уровня, которая как раз тестами на нижних уровнях не покрывается.
Тут речь идёт о том, что _все_ тесты должны быть запущены и успешно пройдены до того, как код увидят юзеры. А у вас это настолько "задешево", что вы не можете себе этого позволить и выполняеете acceptance частично.

P>·>Я под интеграционным кодом подразумеваю который вызывает "те ошибки, которые проходят мимо ваших моков". Так вот мимо моков проходят ошибки в доле процента кода, который выполняется только при реальном старте приложения, код в том самом composition root.

P>Ну да, вы выдумали какое то своё определение интеграционного кода.

Да мне похрен на определения. Я написал суть. Но на суть тебе плевать, придираешься к терминологии.

P>А ошибки с построением запроса фильтров, они как, по вашему, где ходят?

Не знаю где они у вас ходят. Судя по тому что ты рассказал — на проде.

P>·>У технологического кода другой бизнес-домен. Технологический код, который, скажем, реплицирует базу записей в несколько датацентров — оперирует не бизнес-сущностью лайка, а "запись данных", "датацентр" и т.п. А дальше уже идёт интеграция, когда мы в качестве записи берём лайк и получаем надёжное реплицированное хранилище лайков. У тебя же твой билдер билдит конкретный запрос сущности из бизнес-домена, но вместо ассертов бизнес-сущностей ты тестируешь технические детали.

P>Потому, что покрывать тестами нужно все критические свойства, а не ограничиваться только теми, что в спеке написаны.
Так ты не те свойства покрываешь и не так.

P>·>Только тот код, что в composition root сложно покрывается тестами, т.к. там создаются и инжектятся реальные объекты и требует дорогого e2e тестирования. Остальное — неинтересно, ибо быстро, тривиально и надёжно тестируется с помощью моков.

P>·>Если вы не в сосотянии протестировать контроллер, роутер, юзекйс валидацию и сериализацию без e2e — у вас проблемы.
P>Успокойтесь, e2e это не для теста контролера. Наверное вы забыли, что я вам предлагал тестировать валидацию юнит-тестами?
А чем тестируется контроллер-то?

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

P>·>Ты предложил прогонять не все тесты перед выкаткой в прод.
P>Я вам даже рассказал как именно — все ваши тесты вместе взятые пускаете как сейчас. Допустим их 1000 штук. Вот эту 1000 оставьте и не трогайте.
P>А вот далее вы добавляете новый вид тестов, которые будут запускаться регулярно после деплоя. Их штук 10, новых.
P>Итого — 1000 пускаете как раньше, + 10 новых регулярно.
Зачем, чтобы что? Гораздо выгоднее вместо этой хрени пойти в паб отметить — эффект гораздо лучше.

P>>>Откуда возьмутся проблемы у юзеров на проде?

P>·>Не все тесты выполнены.
P>Похоже, X + 1 у вас может быть меньше X. Забавно!
Потому что твоё "добавление" имеет отрицательный эффект. Тратит ресурсы с нулевой пользой. Зачем это +1 запускать после релиза, если мы можем себе это позволить запустить до? У вас беда в том, что из-за детских проблем в проекте вы не можете эти "10 новых регулярно" запускать ещё за день перед релизом. Я тыкаю пальчиком в проблемы и пытаюсь объяснить что вам надо исправить, чтобы вы тоже смогли, но ты продолжаешь надувать щёки со своей data complexity.

P>>>Ваши имеющиеся тесты отработали — за счет чего регрессия произойдет?

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

P>·>Те, для которых написаны тесты — ясен пень не доходят. Если какой-либо тест красный для данной версии системы — в прод она не попадёт.

P>Вот и отлично — значит наличие красных тестов наутро сообщит вам о проблеме еще на стейдже.
Зачем наутро? Через 20 минут же, спасибо мокам.

P>...

P>7 редкие ошибки — прод тупо работает на порядки дольше тестового окружения
Это всё задачи мониторинга и операционного контроля, а не тестов. Тестами в принципе такие задачи надёжно не решаются.
Мониторинг и контроль — другие задачи, другие системы со своим набором тестов, тоже выполняемых до деплоя. Что у вас операционные процессы в плачевном состоянии я ещё больше года назад писал.

P>>>И никто, кроме лично вас не утверждает "на проде багов быть не может"

P>·>Ты меня заебал. Не пиши свои бредовые фантазии как мои цитаты. Я такого никогда нигде не утверждал.
P>Смотрите, вот ваши цитата выше:
P>"Те, для которых написаны тесты — ясен пень не доходят(до прода)" — в скобках это контекст обозначил
И? В логику не умеешь? Помогу: это значит, что могут доходить только те баги, для которых не существует тестов. Из чего ты высосал "багов быть не может" — это мне даже неинтересно.

P>А вы утверждаете, что раз есть тест, то такой баги на проде точно нет

Ты в логику не умеешь совершенно. Это значит, что этот тест будет зелёным и на проде тоже. Либо он может быть красным синхронно с какой-нибудь лампочкой в системе мониторинга. Что делает запуск теста — бесполезным, никакой новой информации он дать не может.

P>>>Вы из своих функций вызываете другие свои функции? Вот вам и интеграционный код. Посмотрите, сколько у вас такого.

P>·>Непокрытого лёгкими тестами? Полторы строчки, всё верно.
P>Сюда нужно вписать data complexity. Хороший пример — те самые фильтры.
Какое это имеет отношение к интеграционному коду?

P>>>·>Т.е. у тебя есть ровно те же "репозиторий + бд" тесты?

P>>>Есть конечно же. Только таких тестов немного — они дают слабые гарантии в силу своей косвенности.
P>·>Слабые относительно чего?
P>Относительно других методов. Ваши косвенные тесты упираются в теорему Райса.
Ага-ага, а ваши "прямые" тесты не упираются. Да вы круты неимоверно.

P>А вот синтаксические свойства можно обеспечивать той же статической типизацией. Надеюсь, вам теорема Райса здесь не мешает?

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

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

P>Как это будет с linq вам Синклер рядом показал. Сама задача в том, что фильтровать нужно по выражению от юзера, который а хрен знает что может понавыбирать.
Я указал на недостатки и дыры в таком подходе в ответе ему.

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

P>·>Потому что попытка юзера выполнить функцию проваливается из-за ошибок в синхронизации.
P>Необязательно, перестаньте фантазировать.
Ну так и моки падают необязательно, перестаньте фантазировать.

P>·>Ещё раз, эта проблема ассертами в тестах не решается.

P>В том то и дело. А что у вас есть кроме тестов?
Всё что надо, не волнуйся, но это оффтоп. Если интересно, заводи новую тему.

P>·>Верно. Но ты не догоняешь, что во 2м пункте можно просто ассертить число записей, с тем же успехом, но лучшим результатом.

P>Что бы ассертить число записей нужно знать комбинацию которой у вас точно нет.
Нужно знать ровно то же, что и у вас.

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

P>·>Не потому что пустое место, а потому что мы тут обсуждаем тесты и разные подходы к тестированию, а 1..3 — оффтопик. Но ты всё не успокоишься и продолжаешь словоблудить.
P>Никакой это не оффтопик. пп 1 2 и 3 говорят о том, что можно изменить дизайн, и ваши моки будут больше не нужны.
Может быть, но тебе не удалось. Ни для примере Буравчика, ни в примере Фаулера.

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

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

P>·>_такой_ тест не нужен. Нужен другой тест.

P>Другой, которого вы так и не показали?
Показал.

P>·>Т.е. твои тесты — проверяют меньше, чем мои. Мои, помимо того, что кол-во записей ограничивается 10ю так же протестирует и валидность запросов, подстановку парамов и т.п.

P>Мои тесты фиксируют структуру решения. Ваши без конкретного перечня комбинаций ничего не гарантируют.
Гарантируют то же что и твои.

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

P>·>Ты о чём? Что за исключённые тесты?
P>Закоментили, удалили, исправили ожидания. Если вы видите, что тест на .top убрали, надо пойти и дать по шее.
Детский сад какой-то.

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

P>·>Завидуешь, да? Ну вообще говоря acceptance гоняется на кластере, вот и укладывается в пол часа.
P>Полчаса это десятки минут. e2e это вобщем тот же acceptance. Если у вас есть такое, чего же вы здесь выступаете?
Выступаю о том, что это всё выполняется ещё до деплоя. Не нужно выполнять ничего после деплоя (ок, один smoke test), и уж тем более частично по random.

P>·>В тесте же конкретный сценарий — запускаем код, дождались завершения, проверяем, выполнилась ли инструкция X. Никакой проблемы останова.

P>Вы успешно опровергли теорему Райса


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

P>·>Так ты зачем-то синтаксис и проверяешь "есть ли в коде .top(10)".
P>В том то и дело — я проверяю структуру, а вы пытаетесь тестами семантику проверять. Отсюда ясно, что вашими тестами вы в теорему Райса упретесь гораздо раньше
Ты не проверяешь структуру, ты копипастишь её из прод-кода в тест-код. Т.е. для надёжности для надёжности пишешь пишешь дважды ровно то же самое то же самое. А то вдруг чего. А то вдруг чего.

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

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

P>>>Там, где у автоматических тестов начинаются недостатки, у других методов будут преимущества.

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

P>·>Главное пока умалчиваешь: тест как выглядит для этого кода? Ну где там deep.eq, pattern или что?

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>expect(builder({ параметры })).to.deep.eq(request)
P>

P>вот так мы можем проверить, добавляет ли билдер top или нет. Билдер будет примерно как у Синклера.
Не понял. Где тут прод-код, где тут тест-код? Напиши однозначно — код под тестом и сам тест.

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

P>·>Там нет ответа на этот вопрос. Там сказано "далее ...бд минимально заполненой", а что было с бд до "далее" — ты надёжно скрываешь.
P>Забавно, вы вырезали часть цитаты, и не знаете, что было в той части, что вы же и выбросили? Ищите — я для вас синим подсветил
Нашел. Там нет ответа на мой вопрос.

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

P>Пример у вас есть?
Всё тот же, не теряй контекст.

P>>>Вам нужно знание о том, как устроены четные и нечетные — у одних младший бит 1, у других — младший бит 0

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

P>>>В переводе на фильтры — у запроса всегда есть лимит.

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

P>А вот где взять запрос, у которого будет нарушено свойство длины — загадка

Что за свойство длины?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[92]: Что такое Dependency Rejection
От: · Великобритания  
Дата: 11.03.24 14:33
Оценка:
Здравствуйте, Sinclair, Вы писали:

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

S>·>Я предлагаю тестировать не моменты, а бизнес-требования. Не бывает в реальности таких требований как "построить кусочек запроса".
S>Это игра словами.
Это разница между хрупкими тестами, которые тестируют детали реализации и полезными тестами, которые тестируют аспекты поведения.

S>·>Вот только невозможно проверить в _тесте_, что .top(10) действительно добавляется к _любому_ запросу.

S>Я же показал, как именно проверить. Какое место осталось непонятным?
Как проверить что к _любому_ запросу. В твоём _тесте_ проверяется только для одного конкртетного запроса с одним из заданных в тесте criteria.

S>·>Это уже лажа. addLimit это метод некоего QueryBuilder, некий библиотечный метод, который уже имеет свои тесты, включая эти твои отрицательные аргументы. Мы полагаемся, что он работает в соотвествии с контрактом когда им пользуемся.

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

S>·>А зачем этот тест нужен-то? Убедиться, что в двух строчках кода одна из них действительно addLimit? Это и так напрямую явно видно в коде и каждый заметит наличие-отсутствие этого вызова и в PR диффе.

S>Ну вы же почему-то не верите в то, что в двух строчках кода в контроллере берётся именно request.ClientTime и требуете, чтобы это было покрыто тестами

S>·>
S>·>IQueryable<User> buildQuery(UserFilter criteria, int pageSize)
S>·>    => someMagicalCondition(criteria) ? buildOptimizedQuery(criteria) : buildWhere(criteria).addLimit(pageSize);
S>·>

S>·>И твои супер-тесты это не обнаружат.
S>У меня не супер-тесты, а самые примитивные юнит-тесты.
S>·>Т.е. твой тест даст false negative. Или тебе известна методология тестирования кода на простоту и линейность?
S>Основная методология — такая же, как вы привели выше. Каждый заметит наличие-отсутствие этого вызова и в PR диффе, поэтому такой код не пройдёт ревью. От автора потребуют переместить свой buildOptimizedQuery внутрь buildWhere. Если у нас нет под рукой theorem prover, то надёжных автоматических альтернатив нет.
Да банально скобочки забыты вокруг ?: — ошиблись в приоритете операций, должно было быть (someMagicalCondition(criteria) ? buildOptimizedQuery(criteria) : buildWhere(criteria)).addLimit(pageSize). Ведь проблема в том, что мы не описали в виде теста ожидаемое поведение. А тестирование синтаксиса никакой _новой_ информации не даёт, синтаксис и так виден явно в диффе.

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

В этом и суть что с т.з. _кода теста_ — проверять наличие addLimit или size()==10 — даёт ровно те же гарантии корректности лимитирования числа записей. Но у меня дополнительно к этому ещё некоторые вещи проверяются и нет завязки на конкретную реализацию, что делает тест менее хрупким.
Ах да. Вы ведь делаете свои тесты красными? Адекватность теста проверить легко — комментируешь "addLimit" и прогоняешь тесты. Если ничего не покраснело, значит надо чинить тесты.

S>·>Мой поинт в том, что ценным тестом будет проверка что addLimit строчка действительно работает в соответствии с бизнес-требованиями — запустить на 11 записях и заассертить, что вернулось только 10 как прописано в FRD — это цель этой строчки, а сама по себе строчка — это средство. И заметь, такой 11/10 тест достаточно сделать для ровно той же комбинации фильтров что и в твоём тесте на наличие addLimit в коде, никакой экспоненциальности которой ты грозишься.

S>Ну это же просто обман. Смотрите за руками: если комбинация фильтров, которая использовалась в тесте на addLimit, попадает под условия someMagicalCondition, то тест сразу сфейлится.
И мой, и твой.

S>А если не попадает — то ващ тест точно так же вернёт 10 записей, и всё.

Верно. Т.е. оба подхода при прочих равных этот аспект проверяют ровно так же. Но твой тест покрывает меньше аспектов.

S>·>т.е. тесты которые так же продолжают работать и ожидаются, что должны быть зелёными без каких-либо изменений после рефакторинга. Такие тесты нужны в любом случае. А твой тест даст false positive и будет просто выкинут. ·>Напишут взамен другой тест, в который закопипастят .contains("rownum < 10") перепутав что rownum zero- или one- based. Отличить на ревью корректность <= от < гораздо сложнее, чем условно save(...11 records...); assert find(10).size() == 10; assert find(5).size() == 5;.

S>Если в проекте завёлся вредитель, то никакими тестами вы его не поймаете. Он просто возьмёт и сломает всё, что угодно. Сейчас вы мне рассказываете, что ловите баги при помощи review кода.
Причём тут вредители? Я очень часто путаю == и != или < и > в коде. Поэтому ещё один способ выразить намерение кода в виде тестового примера с конкретным значением помогает поймать такие опечатки ещё до коммита. Может ты способен в сотне строк обнаружить неверный оператор или забытые строки приоритета, но я на такое не тяну. Поэтому мне надо это дело запустить и просмотреть на результат для конкретного примера.

S>Ну, вот и в нашем случае делается review кода, только код — простой, а за наращивание сложности на ровном месте можно бить человека палкой.

Так ведь ревью делается и для тестов, и для кода. Мне по коду теста сразу видно, что find(10).size() == 10 — правильно. А вот уверено решить что должно быть "rownum > 10", "rownum <= 10" или "rownum < 10" — лично я не смогу, тем более как ревьювер большого PR. Если ты и все члены твоей команды так умеют, то могу только позавидовать.

S>·>Это какой-то тривиальный случай. Это значит не то, что мы магически сделали функцию линейной, а что никаких зависимостей между фильтрами нет в требованиях и никаких 2^N комбинаций просто не нужно, где тут эта ваша грозная data complexity — совершенно неясно.

S>Ну так этой complexity нету как раз потому, что мы её факторизовали. И сделали это благодаря тому, что распилили сложную функцию на набор простых частей.
Какую сложную функцию? Вы просто переписали кривой код на прямой. Тесты тут причём?

S>А вы предлагаете тестировать весь монолит — в рамках него вы не можете полагаться ни на какую независимость. Ну, вот так устроена pull-модель — вам надо построить полный фильтр и выполнить запрос, и никакого способа проверить первую половину запроса отдельно от второй половины запроса не существует.

Я такого не предлагаю.

S>·>В реальности у тебя могут быть хитрые зависимости между параметрами фильтра и вот тут и полезут неявные комбинации экспоненциально всё взрывающие и код несводимый к линейной функции, а будет хитрая if-else-switch лесенка.

S>Нет, по-прежнему будет простая линейная цепочка, только кусочки её будут чуть более длинными.
Да пожалуйста, если получится.

S>·>Гарантию того, что у тебя works as coded. Зато отличить u => u.LastLoginTimeStamp >= criteria.MinLastLoginTimestamp.Value) от u => u.LastLoginTimeStamp <= criteria.MinLastLoginTimestamp.Value) такие тесты не смогут, т.к. пишутся как правило копипастом. На ревью очепятку очень вряд ли кто-то заметит.

S>Если пишут разные люди, то заметят.
Код и тесты обычно пишет один человек. Или вы парное программирование используете?

S>Ну и, опять же, у нас код каждой функции — очень короткий. Там всего-то пара операций.

Если код функции котороткий, то значит у тебя такхи функций овердофига. И в каждой из паре операций можно сделать очепятку.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[95]: Что такое Dependency Rejection
От: Pauel Беларусь http://blogs.rsdn.org/ikemefula
Дата: 11.03.24 19:38
Оценка:
Здравствуйте, ·, Вы писали:

P>>·>smoke tests — как вишенка на торт, но в остальном — проверять две вещи: части работают, части собираются между собой нужным образом.

P>>Что это за тесты, кто их выполняет?
·>CI/CD

Чем ваши acceptance и smoke отличаются от e2e ?

P>>Минорная — это значит, что есть вещи более важные

P>>Если у вас нет хотя бы одного 1..4 никакие тесты вам не помогут, хоть обмажтесь.
·>А без тестов 1-6 уйдут лесом. Единственное, что может не сразу, но уйдут точно.

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

P>>Похоже, ваш капасити переполнен три месяца назад. Вы здесь, извините, порете отсебятину.

·>У тебя память как у рыбки. Цитирую:
·>

·>·>потенциально может быть null — ты не дождёшься окончания.


Я шота не понял ваш аргумент.

P>>Какие именно поля на null ? ui, dto, bl, конфиг, бд?

·>Перечитай топик, всё написано. Твоя цитата:
·>

·>...в составе юнит-тестов, где нас интересуют подробности
·>— а что если в колонке пусто


Непонятный аргумент. Подробнее никак?

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

·>Таких примитивных тривиальных багов как у вас — не бывает. Ибо это всё лечится элементарно.

Видите — я угадал.

·>Подавляющее большинство багов — неверно поняли спеку и реализовали поведение не такое, какое хотел юзер. Но такие баги в большинстве ловятся на этапе демо.


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

·>Так фоновой процессинг н е с божьего попущения работает, а с того же мочёного источника времени.


Вы зачем то снова пустились в фантазирование. Юзер у вас что, на бакенде сидит? Нет ведь. Он в каком то клиентском приложении. Вот там и ищите фоновый процессинг.
Я вам просто пример привел, откуда может прийти проблема, которая в ваших моках никак не отражена.

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

·>Не понял. По ошибке указали в прод конфиге не 30 минут, а 300?

В проде указали 30 минут, а из за мержа конфигов у вас стало 300, например, потому что это дефолтное значение. Это снова пример проблемы которая идет мимо ваших моков.

·>На это дело есть специальный процесс, который показывает diff конфига между продом и тестом. Каждая разница должна быть проревьювена и заапрувлена.

Вот вам дифф ничего и не показал — всё кошерно. А на проде дефолтное значение перетерло значение из конфига. Упс.

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

·>Детский сад. Не используйте глобальные переменные.

Забавная телепатия. Где вы увидели глобальные переменные?

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

·>Забитость очереди — это красный сигнал в мониторинге.

Именно что в мониторинге, а не в ваших тестах на моках. И только под нагрузкой.

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

·>"строчка тестового кода" это как?! Тестовый код физически лежит в другом каталоге от прода.

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

P>>Проблему нужно решать не моками, а дизайном.

·>Ага, т.е. опять оффтоп. Мы же тут обсуждаем тесты, а не дизайн.

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

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

·>Мде. Тупо — не используйте глобальные переменные.

Это всё мимо. Экземпляр UI приложения это штука глобальная, вне зависимости от того, где вы держите переменную app — глобально или в стеке функции main или хоть вообще нигде. Это ж не сервер, где на запрос можно поднимать хоть целый процесс.
Вопрос в том, как у вас сделано разделение фонового и интерактивного приложений.

P>>А если время сессии это критическая вещь, то e2e ну просится — архитектура аутентификации всегда сложная, там много чего может пойти странными путями.

·>Это легко лечится: Любая вещь, сложная или не сложная — первый вопрос на который должен быть дан ответ прежде чем приступать к обсуждению дизайна и уж тем более реализации — как мы это можем покрыть быстрыми автотестами?

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

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

·>Мониторинг обнаруживает проблемы мгновенно, в отличие от.

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

·>e2e чего? e2e — это сценарий, когда какой-то тестовый юзер чего-то сделал и проверяем, что система совершила ожидаемые действия. А statement создаётся для реальных юзеров для действий которые они совершили.


Вы что, повсюду в бизнес-логике пишете "if user.isTest() " ? Очень вряд ли. Интеграция для тестового юзера и реального будет та же самая. Соответственно, обнаруженные проблемы практически наверняка будут играть и для тестовых юзеров.

·>Тут речь идёт о том, что _все_ тесты должны быть запущены и успешно пройдены до того, как код увидят юзеры. А у вас это настолько "задешево", что вы не можете себе этого позволить и выполняеете acceptance частично.


Мы уже выясняли — вы держите только ту часть acceptance, которую можно выполнить за полчаса. А другие на идут дальше этого 1%

P>>Ну да, вы выдумали какое то своё определение интеграционного кода.

·>Да мне похрен на определения. Я написал суть. Но на суть тебе плевать, придираешься к терминологии.

Через ваши доморощенные определения крайне трудно продираться.

P>>·>Если вы не в сосотянии протестировать контроллер, роутер, юзекйс валидацию и сериализацию без e2e — у вас проблемы.

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

Интеграционными — средний уровень в пирамиде. Я ж вам объяснял уже.

P>>Итого — 1000 пускаете как раньше, + 10 новых регулярно.

·>Зачем, чтобы что? Гораздо выгоднее вместо этой хрени пойти в паб отметить — эффект гораздо лучше.

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

P>>Похоже, X + 1 у вас может быть меньше X. Забавно!

·>Потому что твоё "добавление" имеет отрицательный эффект. Тратит ресурсы с нулевой пользой. Зачем это +1 запускать после релиза, если мы можем себе это позволить запустить до?

Вы же сами сказали — у вас ажно 1% покрыт. Вот на остальные 99% и нужно придумать чтото получше чем есть у вас.

P>>Откуда возьмется регрессия?

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

Теорема Райса с вами не согласна. Нету у вас 100% надежного способа гарантировать даже true из boolean.

Баги имеют свойство воспроизводиться повторно, например, потому что
1 часто у бага больше одной причины — далеко не все они дружно сами себя документируют в тестовом прогоне
2 разница прода и стейджа (нагрузка, время, объемы данных, итд)
3 итд

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

·>Зачем наутро? Через 20 минут же, спасибо мокам.

Вашу интеграцию тестируют acceptance и smoke. я вот не удивлюсь, если окажется что ваши acсeptance это ровно то же, что и e2e

P>>"Те, для которых написаны тесты — ясен пень не доходят(до прода)" — в скобках это контекст обозначил

·>И? В логику не умеешь? Помогу: это значит, что могут доходить только те баги, для которых не существует тестов. Из чего ты высосал "багов быть не может" — это мне даже неинтересно.

А также те, для которых тесты существуют, но причин более одной, разница прода и стейджа итд.

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


Теорема Райса с вами не согласна. Нету у вас 100% надежного способа гарантировать даже true из boolean.

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


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

P>>>>Вы из своих функций вызываете другие свои функции? Вот вам и интеграционный код. Посмотрите, сколько у вас такого.

P>>·>Непокрытого лёгкими тестами? Полторы строчки, всё верно.
P>>Сюда нужно вписать data complexity. Хороший пример — те самые фильтры.
·>Какое это имеет отношение к интеграционному коду?

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

P>>Относительно других методов. Ваши косвенные тесты упираются в теорему Райса.

·>Ага-ага, а ваши "прямые" тесты не упираются. Да вы круты неимоверно.

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

Структурная эквивалентность, синтаксис теореме Райса не подчиняются.
А вот ваши тесты это чистой воды семантические свойства которые полностью подчиняются теореме Райса

P>>Проектирование — формальные методы, например, доказательства тех или иных свойств.

·>Оффтоп, т.к. мы обсуждаем методы тестирования.

Это вы так хотите. Я все время говорю про подход к дизайну. Dependency Rejection — это принцип дизайна прежде всего

P>>Как это будет с linq вам Синклер рядом показал. Сама задача в том, что фильтровать нужно по выражению от юзера, который а хрен знает что может понавыбирать.

·>Я указал на недостатки и дыры в таком подходе в ответе ему.

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

P>>В том то и дело. А что у вас есть кроме тестов?

·>Всё что надо, не волнуйся, но это оффтоп. Если интересно, заводи новую тему.

Я вам уже третий месяц предлагаю показать это "всё что надо" и вы приводите только тесты, которые требуют комбинаций которых у вас нет

·>Может быть, но тебе не удалось. Ни для примере Буравчика, ни в примере Фаулера.


У Буравчика как раз тот случай, где нужны моки. Я же сразу это сказал, а вы третий месяц срываете покровы.

·>Похрен на дизайн. Ты простую мысль так и не понял. Проверять кол-во записей и проверять наличие top — даёт те же гарантии. Но проверка кол-ва записей проверяет больше.


В том то и дело, что не даёт. У вас нет комбинаций, а потому проверяние количества записей смысла не имеет — в тестовой базе просто нет тех самых данных.

P>>Полчаса это десятки минут. e2e это вобщем тот же acceptance. Если у вас есть такое, чего же вы здесь выступаете?

·>Выступаю о том, что это всё выполняется ещё до деплоя. Не нужно выполнять ничего после деплоя (ок, один smoke test), и уж тем более частично по random.

Вы еще и кластер по acceptance разворачиваете. Или у вас там тоже моки?

P>>В том то и дело — я проверяю структуру, а вы пытаетесь тестами семантику проверять. Отсюда ясно, что вашими тестами вы в теорему Райса упретесь гораздо раньше

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

В прод коде только билдер. Откуда копипаста взялась?

·>Похрен. Идёт речь про тесты. Ты заявляешь "Вы так и не показали ни одного автоматического теста". И что, и не обязан был. Ты не показал вообще никакого теста. И не сможешь, ясен пень.


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

P>>Ручные методы это в т.ч. exploratory, и вообще исследование.

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

Автоматизируется только рутина. Результат exploratory это не автоматический тест, а обозначение проблемы и перечня новых тест кейсов. Будут ли они автоматизироваться, или нет, дело десятое

P>>·>Главное пока умалчиваешь: тест как выглядит для этого кода? Ну где там deep.eq, pattern или что?

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>>expect(builder({ параметры })).to.deep.eq(request)
P>>

P>>вот так мы можем проверить, добавляет ли билдер top или нет. Билдер будет примерно как у Синклера.
·>Не понял. Где тут прод-код, где тут тест-код? Напиши однозначно — код под тестом и сам тест.

Это весь тест. прод код — функция builder. Параметры я опустил, для наглядности, как и filterExpression
Это всё из другого проекта, я им больше не занимаюсь

P>>Забавно, вы вырезали часть цитаты, и не знаете, что было в той части, что вы же и выбросили? Ищите — я для вас синим подсветил

·>Нашел. Там нет ответа на мой вопрос.

Попробуйте заново сформулировать вопрос. А на ваши огрызки цитат ответы ищите в синих буквах.

P>>·>"у запроса всегда есть лимит" в переводе в твоём случае будет "функция isOdd всегда использует битовую операцию взятия младшего бита".

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

Вот вы важное ограничение вашего подхода и нашли — бесконечность.
А вот у пост-условия такого ограничения нет в принципе.
Идея понятна?

P>>А вот где взять запрос, у которого будет нарушено свойство длины — загадка

·>Что за свойство длины?

Известно какое — в память не влазит
Re[96]: Что такое Dependency Rejection
От: · Великобритания  
Дата: 11.03.24 23:23
Оценка:
Здравствуйте, Pauel, Вы писали:

P>>>Что это за тесты, кто их выполняет?

P>·>CI/CD
P>Чем ваши acceptance и smoke отличаются от e2e ?
acceptance и smoke — подвиды e2e. Первые запускаются до деплоя, вторые запускаются сразу после. Если smoke-тест падает — делается откат к предыдущей версии.

P>>>Минорная — это значит, что есть вещи более важные

P>>>Если у вас нет хотя бы одного 1..4 никакие тесты вам не помогут, хоть обмажтесь.
P>·>А без тестов 1-6 уйдут лесом. Единственное, что может не сразу, но уйдут точно.
P>Тесты напрямую не влияют на качество — это просто индикаторы симптомов.
P>Более того, если тесты зеленые, это говорит всего лишь "не обнаружили", а что там на самом деле с софтиной — а хрен его знает, если у вас нет 1-4
P>Хотите повышать качество — нужно проектировать, кодить больше, лучше, помогать другим, документировать, итд, итд.
Угу. Вот только все эти усилия помножатся на ноль без авто-тестов.

P>·>У тебя память как у рыбки. Цитирую:

P>·>

P>·>·>потенциально может быть null — ты не дождёшься окончания.
P>·>

P>Я шота не понял ваш аргумент.
В смысле ты не понял что ты хотел сказать своей цитатой? Ничем помочь не могу.

P>>>Какие именно поля на null ? ui, dto, bl, конфиг, бд?

P>·>Перечитай топик, всё написано. Твоя цитата:
P>·>

P>·>...в составе юнит-тестов, где нас интересуют подробности
P>·>— а что если в колонке пусто
P>·>

P>Непонятный аргумент. Подробнее никак?
Аргумент чего? Это твоя цитата. Что она значит — разбирайся сам.

P>·>Подавляющее большинство багов — неверно поняли спеку и реализовали поведение не такое, какое хотел юзер. Но такие баги в большинстве ловятся на этапе демо.

P>Это значит, что у вас простой кейс — для всего дадена спека. Что делать будете, кода результат вашей работы это и спека, и софт по ней?
Ещё проще.

P>·>Так фоновой процессинг н е с божьего попущения работает, а с того же мочёного источника времени.

P>Вы зачем то снова пустились в фантазирование. Юзер у вас что, на бакенде сидит? Нет ведь. Он в каком то клиентском приложении. Вот там и ищите фоновый процессинг.
В смысле фоновой процессинг в 3rd party внешней системе? Ну тогда это их проблема, поведение нашего api задокументировано.

P>Я вам просто пример привел, откуда может прийти проблема, которая в ваших моках никак не отражена.

Поведение нашей системы ограничивается нашей системой. Если другие делают ошибки, то в лучшем случае мы им поможем их найти.

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

P>·>Не понял. По ошибке указали в прод конфиге не 30 минут, а 300?
P>В проде указали 30 минут, а из за мержа конфигов у вас стало 300, например, потому что это дефолтное значение. Это снова пример проблемы которая идет мимо ваших моков.
Дифф конфигов прода и теста покажет отстутствие параметра.

P>·>На это дело есть специальный процесс, который показывает diff конфига между продом и тестом. Каждая разница должна быть проревьювена и заапрувлена.

P>Вот вам дифф ничего и не показал — всё кошерно. А на проде дефолтное значение перетерло значение из конфига. Упс.
Если диффа нет, это значит что значение одинаково.

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

P>·>Детский сад. Не используйте глобальные переменные.
P>Забавная телепатия. Где вы увидели глобальные переменные?
В DateTime.Now().

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

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

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

P>·>"строчка тестового кода" это как?! Тестовый код физически лежит в другом каталоге от прода.
P>Очень просто. Код для тестов и тесты для кода это две большие разницы, как говорят в Одессе.
P>Например, разработчик кое что подтюнил в коде, что бы легче было воспроизвести другую багу. И забыл это убрать.
Треш какой-то у вас творится.

P>>>Проблему нужно решать не моками, а дизайном.

P>·>Ага, т.е. опять оффтоп. Мы же тут обсуждаем тесты, а не дизайн.
P>Вы до сих пор название топика не вкурили? dependency rejection это целиком про дизайн.
Это вроде давно обсудили. Ты потом переключился на моки.

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

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

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

P>·>Мде. Тупо — не используйте глобальные переменные.
P>Это всё мимо. Экземпляр UI приложения это штука глобальная, вне зависимости от того, где вы держите переменную app — глобально или в стеке функции main или хоть вообще нигде. Это ж не сервер, где на запрос можно поднимать хоть целый процесс.
Мде. Видимо вообще вы с Sinclair не вдупляете что такое глобальные переменные. Поэтому и используете, не видя разницы.

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

P>·>Мониторинг обнаруживает проблемы мгновенно, в отличие от.
P>Да, вы эту сказку много раз повторяли — у вас мониторинг любую даже гипотетически возможную проблему обнаруживает сразу.
Это ты опять врёшь, я такое не говорил. А у вас тестинг занимается обнаружением любых гипотетических проблем, да?

P>·>e2e чего? e2e — это сценарий, когда какой-то тестовый юзер чего-то сделал и проверяем, что система совершила ожидаемые действия. А statement создаётся для реальных юзеров для действий которые они совершили.

P>Вы что, повсюду в бизнес-логике пишете "if user.isTest() " ? Очень вряд ли. Интеграция для тестового юзера и реального будет та же самая. Соответственно, обнаруженные проблемы практически наверняка будут играть и для тестовых юзеров.
Т.е. в конце месяца ты узнаешь, что у вас сломано создание statement? Это уже очень поздно. Ты получишь злобное письмо как минимум от каждого десятого юзера.

P>·>Тут речь идёт о том, что _все_ тесты должны быть запущены и успешно пройдены до того, как код увидят юзеры. А у вас это настолько "задешево", что вы не можете себе этого позволить и выполняеете acceptance частично.

P>Мы уже выясняли — вы держите только ту часть acceptance, которую можно выполнить за полчаса. А другие на идут дальше этого 1%
Нет, мы дизайним так, чтобы любой acceptance можно было выполнить за пол часа, даже для сценариев которые требуют sleep(1 month). Время-то мочёное.

P>>>Ну да, вы выдумали какое то своё определение интеграционного кода.

P>·>Да мне похрен на определения. Я написал суть. Но на суть тебе плевать, придираешься к терминологии.
P>Через ваши доморощенные определения крайне трудно продираться.
Дык все эти определения доморощенные.

P>>>Успокойтесь, e2e это не для теста контролера. Наверное вы забыли, что я вам предлагал тестировать валидацию юнит-тестами?

P>·>А чем тестируется контроллер-то?
P>Интеграционными — средний уровень в пирамиде. Я ж вам объяснял уже.
Какая часть приложения требуется для прогона такого теста?

P>>>Итого — 1000 пускаете как раньше, + 10 новых регулярно.

P>·>Зачем, чтобы что? Гораздо выгоднее вместо этой хрени пойти в паб отметить — эффект гораздо лучше.
P>Что вам непонятно было в прошлый раз? Вы продолжаете задавать один и тот же вопрос, получаете один и тот же ответ. Чего вы ждете?
Что вам непонято было в прошлый раз? Вы продолжаете гнать пургу про запуск тестов в проде случайным образом в случайные моменты времени.

P>>>Похоже, X + 1 у вас может быть меньше X. Забавно!

P>·>Потому что твоё "добавление" имеет отрицательный эффект. Тратит ресурсы с нулевой пользой. Зачем это +1 запускать после релиза, если мы можем себе это позволить запустить до?
P>Вы же сами сказали — у вас ажно 1% покрыт. Вот на остальные 99% и нужно придумать чтото получше чем есть у вас.
smoke-тестом — да, ~1%. А больше и не надо.

P>>>Откуда возьмется регрессия?

P>·>Если эти ваши "новые" тесты могут упасть в принципе, то значит у вас регрессия не найденная тестами до деплоя.
P>Теорема Райса с вами не согласна. Нету у вас 100% надежного способа гарантировать даже true из boolean.
Верно, т.е. эти ваши тесты ничего нового не дадут.

P>Баги имеют свойство воспроизводиться повторно, например, потому что

P>1 часто у бага больше одной причины — далеко не все они дружно сами себя документируют в тестовом прогоне
P>2 разница прода и стейджа (нагрузка, время, объемы данных, итд)
P>3 итд
Я же объяснил, для управления этим используются другие средства вместо прогона тестов.

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

P>·>Зачем наутро? Через 20 минут же, спасибо мокам.
P>Вашу интеграцию тестируют acceptance и smoke. я вот не удивлюсь, если окажется что ваши acсeptance это ровно то же, что и e2e
Зависит от терминологии. Acceptance работают с моками внешних систем. Это считается как e2e?

P>>>"Те, для которых написаны тесты — ясен пень не доходят(до прода)" — в скобках это контекст обозначил

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

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

P>Теорема Райса с вами не согласна. Нету у вас 100% надежного способа гарантировать даже true из boolean.
Если только недетерминизм есть какой-то, из-за ошибок в многопоточке, например. Но такое тестами и не найти, тем более на проде. Если только случайно повезёт.

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

P>А еще он поможет подсветиться лампочке в мониторинга до того, как юзер напорется на проблему, а не после
Это как? Вызвав нехватку ресурсов?

P>>>Сюда нужно вписать data complexity. Хороший пример — те самые фильтры.

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

P>>>Относительно других методов. Ваши косвенные тесты упираются в теорему Райса.

P>·>Ага-ага, а ваши "прямые" тесты не упираются. Да вы круты неимоверно.
P>Вы одной ногой понимаете, а другой — уже нет.
P>Мой подход того же поля что и статическая типизация — проверяет структуру, синтаксис.
Для проверки статической типизации изобрели компиляторы. Тесты статическую типизацию проверять не могут в принципе.

P>Структурная эквивалентность, синтаксис теореме Райса не подчиняются.

Это и означает, что ваш тест проверяет, что works as coded. Абсолютно бесполезное занятие.

P>А вот ваши тесты это чистой воды семантические свойства которые полностью подчиняются теореме Райса

Так тесты и должны проверять семантику. Не гарантировать корректность семантики, а проверять для конкретных примеров. Тесты делают не с целью верификации свойств кода, а с целью упрощения написания кода, как исполнимая документация.

P>>>Как это будет с linq вам Синклер рядом показал. Сама задача в том, что фильтровать нужно по выражению от юзера, который а хрен знает что может понавыбирать.

P>·>Я указал на недостатки и дыры в таком подходе в ответе ему.
P>Ваши тесты еще хуже. По уму, нужно и то и другое. Но вы почему то выбираете худшее из двух.
Наличие того, делает ненужным другое.

P>Ваши тесты ничего не дают без знания конкретных комбинаций на которых можно отработать тест

Ваши тоже.

P>·>Может быть, но тебе не удалось. Ни для примере Буравчика, ни в примере Фаулера.

P>У Буравчика как раз тот случай, где нужны моки. Я же сразу это сказал, а вы третий месяц срываете покровы.
Мне ещё не довелось увидеть случая где не нужны моки. И ты правду скрыавешь.

P>·>Похрен на дизайн. Ты простую мысль так и не понял. Проверять кол-во записей и проверять наличие top — даёт те же гарантии. Но проверка кол-ва записей проверяет больше.

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

P>>>Полчаса это десятки минут. e2e это вобщем тот же acceptance. Если у вас есть такое, чего же вы здесь выступаете?

P>·>Выступаю о том, что это всё выполняется ещё до деплоя. Не нужно выполнять ничего после деплоя (ок, один smoke test), и уж тем более частично по random.
P>Вы еще и кластер по acceptance разворачиваете. Или у вас там тоже моки?
Да, конечно. Например, для источника времени мок красиво называется tardis.

P>>>В том то и дело — я проверяю структуру, а вы пытаетесь тестами семантику проверять. Отсюда ясно, что вашими тестами вы в теорему Райса упретесь гораздо раньше

P>·>Ты не проверяешь структуру, ты копипастишь её из прод-кода в тест-код. Т.е. для надёжности для надёжности пишешь пишешь дважды ровно то же самое то же самое. А то вдруг чего. А то вдруг чего.
P>В прод коде только билдер. Откуда копипаста взялась?
В прод-коде у тебя будет реализация, "LIMIT 10", в тест-коде у тебя будет .eq("...LIMIT 10").

P>·>Похрен. Идёт речь про тесты. Ты заявляешь "Вы так и не показали ни одного автоматического теста". И что, и не обязан был. Ты не показал вообще никакого теста. И не сможешь, ясен пень.

P>В данном случае я вам все показал, кроме реального запроса, который будет построен билдером. Там естественно, будет не limit, и не набор AND OR итд.
А что?

P>>>Ручные методы это в т.ч. exploratory, и вообще исследование.

P>·>И? Из ручного тут лишь само создание автоматических тестов. Да, если ты не знал — код тестов пишут вручную. Если вы их герените автоматом, то не делайте так больше.
P>Автоматизируется только рутина. Результат exploratory это не автоматический тест, а обозначение проблемы и перечня новых тест кейсов. Будут ли они автоматизироваться, или нет, дело десятое
У вас да, наверное. У нас результат именно, что код, который можно просто закоммитить как красный автотест, чтобы кто-то пофиксил, сделав его зелёным.

P>Это весь тест. прод код — функция builder. Параметры я опустил, для наглядности, как и filterExpression

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

P>>>Забавно, вы вырезали часть цитаты, и не знаете, что было в той части, что вы же и выбросили? Ищите — я для вас синим подсветил

P>·>Нашел. Там нет ответа на мой вопрос.
P>Попробуйте заново сформулировать вопрос. А на ваши огрызки цитат ответы ищите в синих буквах.
Используя какую бд проверяется паттерн запроса вручную, _до того_ как появится "тест против бд минимально заполненной под задачу"?

P>>>·>"у запроса всегда есть лимит" в переводе в твоём случае будет "функция isOdd всегда использует битовую операцию взятия младшего бита".

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

P>>>А вот где взять запрос, у которого будет нарушено свойство длины — загадка

P>·>Что за свойство длины?
P>Известно какое — в память не влазит
Что не влазит? Кому не влазит? Нихрена не понял.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[97]: Что такое Dependency Rejection
От: Pauel Беларусь http://blogs.rsdn.org/ikemefula
Дата: 12.03.24 19:15
Оценка:
Здравствуйте, ·, Вы писали:

P>>Чем ваши acceptance и smoke отличаются от e2e ?

·>acceptance и smoke — подвиды e2e. Первые запускаются до деплоя, вторые запускаются сразу после. Если smoke-тест падает — делается откат к предыдущей версии.

Итого — e2e у вас есть.

P>>Тесты напрямую не влияют на качество — это просто индикаторы симптомов.

P>>Более того, если тесты зеленые, это говорит всего лишь "не обнаружили", а что там на самом деле с софтиной — а хрен его знает, если у вас нет 1-4
P>>Хотите повышать качество — нужно проектировать, кодить больше, лучше, помогать другим, документировать, итд, итд.
·>Угу. Вот только все эти усилия помножатся на ноль без авто-тестов.

Помножатся. Только без 1-4 тесты вообще смысла не имеют. Попробуйте писать тесты до появления треботваний, результаты пишите сюда.

P>>·>У тебя память как у рыбки. Цитирую:

P>>·>

P>>·>·>потенциально может быть null — ты не дождёшься окончания.
P>>·>

P>>Я шота не понял ваш аргумент.
·>В смысле ты не понял что ты хотел сказать своей цитатой? Ничем помочь не могу.

Это ваша цитата http://rsdn.org/forum/design/8701337.1
Автор: ·
Дата: 29.02.24

Вот мы и узнали, у кого память как у рыбки. И уже в который раз

P>>·>

P>>·>...в составе юнит-тестов, где нас интересуют подробности
P>>·>— а что если в колонке пусто
P>>·>

P>>Непонятный аргумент. Подробнее никак?
·>Аргумент чего? Это твоя цитата. Что она значит — разбирайся сам.

"а что если в колонке пусто" — вот это вроде бы уже выясняли. Вам еще хочется пройтись по кругу?

P>>Это значит, что у вас простой кейс — для всего дадена спека. Что делать будете, кода результат вашей работы это и спека, и софт по ней?

·>Ещё проще.

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

P>>Вы зачем то снова пустились в фантазирование. Юзер у вас что, на бакенде сидит? Нет ведь. Он в каком то клиентском приложении. Вот там и ищите фоновый процессинг.

·>В смысле фоновой процессинг в 3rd party внешней системе? Ну тогда это их проблема, поведение нашего api задокументировано.

Т.е. вы не понимаете, что фронтенд, мобайл, десктоп может быть частью вашей системы?

P>>В проде указали 30 минут, а из за мержа конфигов у вас стало 300, например, потому что это дефолтное значение. Это снова пример проблемы которая идет мимо ваших моков.

·>Дифф конфигов прода и теста покажет отстутствие параметра.

Как то слишком категорично. Думаете вы один умеете конфиги сравнивать?
Посмотрите в коде — где у вас дефолтные значения для параметров по умолчанию?
У вас что, все 100500 возможных констант исключительно в конфигах?
Дефолтные значения могут подкидываться даже либами, если вы забудете подкинуть значение.

P>>·>Забитость очереди — это красный сигнал в мониторинге.

P>>Именно что в мониторинге, а не в ваших тестах на моках. И только под нагрузкой.
·>Угу, и?

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

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

·>Треш какой-то у вас творится.

Вот снова вы ни о чем.

P>>Вы до сих пор название топика не вкурили? dependency rejection это целиком про дизайн.

·>Это вроде давно обсудили. Ты потом переключился на моки.

Это все в контексте дизайна который определяется тестами. Написали под моки — значит пропихивать зависимости так, а не иначе. И всё, приплыли.

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

·>Нет никакого дизайна под моки. Есть внешние системы — будут моки.

У вас именно так — вы считаете моки единственным инструментом для работы с зависимостями.

P>>Это всё мимо. Экземпляр UI приложения это штука глобальная, вне езависимости от того, где вы держите переменную app — глобально или в стеке функции main или хоть вообще нигде. Это ж не сервер, где на запрос можно поднимать хоть целый процесс.

·>Мде. Видимо вообще вы с Sinclair не вдупляете что такое глобальные переменные. Поэтому и используете, не видя разницы.

Скорее это вы почему то не понимаете, что мир не сводится к вашему проекту.
Подозреваю, вы и под глобальными переменными вы подразумеваете чтото другое.

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

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

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

P>>Вы что, повсюду в бизнес-логике пишете "if user.isTest() " ? Очень вряд ли. Интеграция для тестового юзера и реального будет та же самая. Соответственно, обнаруженные проблемы практически наверняка будут играть и для тестовых юзеров.

·>Т.е. в конце месяца ты узнаешь, что у вас сломано создание statement? Это уже очень поздно. Ты получишь злобное письмо как минимум от каждого десятого юзера.

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

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

P>>Мы уже выясняли — вы держите только ту часть acceptance, которую можно выполнить за полчаса. А другие на идут дальше этого 1%

·>Нет, мы дизайним так, чтобы любой acceptance можно было выполнить за пол часа, даже для сценариев которые требуют sleep(1 month). Время-то мочёное.

Значит это никакой не acceptance если вы мокаете время. Вероятно, это ваша местная специфика.

P>>Интеграционными — средний уровень в пирамиде. Я ж вам объяснял уже.

·>Какая часть приложения требуется для прогона такого теста?

Повторяюсь уже
new controller(параметры)

Отдельные части это технически вообще как юнит-тесты.

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

·>Что вам непонято было в прошлый раз? Вы продолжаете гнать пургу про запуск тестов в проде случайным образом в случайные моменты времени.

Прогон тестов на проде это не моя придумка. Уже лет десять назад было довольно популярной практикой.

P>>Вы же сами сказали — у вас ажно 1% покрыт. Вот на остальные 99% и нужно придумать чтото получше чем есть у вас.

·>smoke-тестом — да, ~1%. А больше и не надо.

А acceptance сколько покрывает?

P>>Теорема Райса с вами не согласна. Нету у вас 100% надежного способа гарантировать даже true из boolean.

·>Верно, т.е. эти ваши тесты ничего нового не дадут.

мой подход сродни статической типизации — на него теорема Райса не действует

·>Я же объяснил, для управления этим используются другие средства вместо прогона тестов.


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

P>>Вашу интеграцию тестируют acceptance и smoke. я вот не удивлюсь, если окажется что ваши acсeptance это ровно то же, что и e2e

·>Зависит от терминологии. Acceptance работают с моками внешних систем. Это считается как e2e?

Условно, да

P>>А также те, для которых тесты существуют, но причин более одной, разница прода и стейджа итд.

·>Разница в чём? У бинарников полное совпадение до бита. Отличаться могут только конфиги, и это контролируется diff-ом.

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

·>Если только недетерминизм есть какой-то, из-за ошибок в многопоточке, например. Но такое тестами и не найти, тем более на проде. Если только случайно повезёт.


Именно для этого некоторые конторы в момент максимальной нагрузки запускают тесты прода. Это не шутка.
Таким образом тестируется система в нагрузке.

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

·>Это как? Вызвав нехватку ресурсов?

Например, потому что запускается в момент большой загрузки.

P>>Комбинаций у вас нет. Так?

·>Комбинации есть ровно те, что что и у вас.

Так и у меня их нет. И мой подход основан на пост-условиях. Это почти что статическая типизация, с некоторым упрощением.

P>>Мой подход того же поля что и статическая типизация — проверяет структуру, синтаксис.

·>Для проверки статической типизации изобрели компиляторы. Тесты статическую типизацию проверять не могут в принципе.

В том то и дело. И пост-условия тесты тоже заменить не могут.
1 типизация, пост-условия, инварианты, пред-условия, структурная эквивалентность используются, когда у нас есть только свойства результата
2 тесты — когда есть часть результата
Т.е. если у нас есть хитрый ORM, то мы можем добавить такой тип Limited<Query, NoLongerThan<10>>

Поскольку такого ORM нет, то вместо статической типизации я подбираю инструмент c похожей механикой — пост-условия, структурная эквивалентность
А вот тесты здесь играют совсем другую роль — они фиксируют наличие пост-условия.

P>>Структурная эквивалентность, синтаксис теореме Райса не подчиняются.

·>Это и означает, что ваш тест проверяет, что works as coded. Абсолютно бесполезное занятие.

work as coded это если копировать sql из приложения в тесты.
А если в приложении билдер, а в тестах — его выхлоп на конкретных параметрах, то мы напрямую проверяем маппер — о большинстве проблем вы узнаете еще до запуска на реальной базе данных


P>>А вот ваши тесты это чистой воды семантические свойства которые полностью подчиняются теореме Райса

·>Так тесты и должны проверять семантику. Не гарантировать корректность семантики, а проверять для конкретных примеров. Тесты делают не с целью верификации свойств кода, а с целью упрощения написания кода, как исполнимая документация.

Ну так конкретных примеров то нет. А раз так — то используем "1 типизация, пост-условия, инварианты, пред-условия, структурная эквивалентность"

P>>Ваши тесты ничего не дают без знания конкретных комбинаций на которых можно отработать тест

·>Ваши тоже.

Вы пока не смогли пример адекватный привести, который сломает хотя бы примитивный limit 10.

P>>У Буравчика как раз тот случай, где нужны моки. Я же сразу это сказал, а вы третий месяц срываете покровы.

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

Потому, что вы изначально думаете в единицах моков, и почти всё к ним и сводите.

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

·>У меня ровно те же комбинации, что будут и в твоих тестах. Отличается только часть в ассертах. Вместо проверки синтаксиса — sql-код запускается и валидируется интересный с тз бизнеса результат.

Комбинации в бд не показывают проблему. Следовательно, вы вытащите её на прод.

Хотите устранить — нужно чтото большее, чем проверка комбинаций

P>>В прод коде только билдер. Откуда копипаста взялась?

·>В прод-коде у тебя будет реализация, "LIMIT 10", в тест-коде у тебя будет .eq("...LIMIT 10").
.
Не будет. Нигде в коде приложения нет явного LIMIT 10. Лимит вычисляется билдером. Будет ли это LIMIT или подзапрос с нужным выражением — дело десятое.
Важно, что лимит будет обязательно. И мне не надо перебирать всю таблицу истинности билдера на интеграционных тестах.

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

·>Ну сделай набросок, чтобы понятно было.

Все что мог, я вам показал. Что еще нужно? Запрос вы видите. Он собирается из разных частей. Каждая тестируется по отдельности.
Главное что бы структура была корректной.

P>>·>Нашел. Там нет ответа на мой вопрос.

P>>Попробуйте заново сформулировать вопрос. А на ваши огрызки цитат ответы ищите в синих буквах.
·>Используя какую бд проверяется паттерн запроса вручную, _до того_ как появится "тест против бд минимально заполненной под задачу"?

P>>А вот у пост-условия такого ограничения нет в принципе.

P>>Идея понятна?
·>Понятна конечно, я это тебе и говорил. Вот только постусловие ты проверить не сможешь тестом.

Тестом я не проверяю пост-условие. Тестом я проверяю, что оно на месте.

P>>·>Что за свойство длины?

P>>Известно какое — в память не влазит
·>Что не влазит? Кому не влазит? Нихрена не понял.

Результат запроса не влазит в память.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.