Обновить
16K+
2
Nikolai Liapin@NickLiapin

SDET

12
Рейтинг
1
Подписчики
Отправить сообщение

Отчасти да - если знать про mapped-адреса, то "::ffff:192.0.2.5" просто добавляется в юнит-кейсы, и после инцидента именно так и делается, это правильный регрессионный тест. Вопрос в том, откуда этот кейс возьмется в списке до инцидента.

Юнит-тест проверяет функцию против нашего представления о входах - что придет строка с чем угодно - да, но "что угодно" в тестах всегда конечный список, который мы сами и придумали. Если про mapped-форму не знаешь, ее в списке не будет, и тест честно зеленый. А интеграционный через сокет проверяет само представление: он приносит в проверку тот вход, который реально отдает ядро, без нашего участия. Баг ведь не в функции сравнения - она корректно сравнивает то, что дали. Баг на стыке "что отдает сокет" и "что ожидает проверка", а стыки юнитами не проверяются по определению, на то они и юниты.

Хороший разбор, взгляд зацепился:

Код отрабатывает штатно, тест на «адрес не из сети — отказать» проходит, ревью проходит тоже.


Есть небольшой опыт в тестировании, это классика: проверку вайтлиста тестируют как чистую функцию - подали строку "192.0.2.5", получили allow, подали "10.0.0.1" - deny, все зеленое. А в проде адрес приходит не строкой из теста, а из сокета, и баг живет ровно на этом стыке. Юнит-тестом его не поймать в принципе, хоть 100% покрытия сделай.

Ловится только интеграционным тестом через реальный сокет: поднимаем сервис на "::", подключаемся по 127.0.0.1 и проверяем, что вайтлист со 127.0.0.1 пропускает. Один такой тест в CI - и вся эта история падает на пулл-реквесте с переездом на dual-stack, а не на заявке от клиента. Но так почти никто не делает: проверка адреса выглядит слишком простой, чтобы гонять ее не юнитом.

А при чём тут СУБД? Это про генерацию тестовых данных. Очень жаль, что вы не потрудились ознакомиться с текстом до комментария.

Статья в целом неплохая, спасибо, хотя по большей части это пересказ уже известных вещей про keyset-пагинацию. Но в коде есть моменты, которые меня смутили. Может я ошибаюсь, поправьте.

  1. В финальном Playwright-тесте по-моему гонка (и это в тесте про гонки). oldResponseSent резолвится сразу после route.fulfill(), то есть ответ браузеру ушел, но приложение его еще не распарсило и DOM не обновило. Негативный toHaveText в этот момент пройдет мгновенно, а баговый append прилетит тактом позже. Тест зеленый, приложение сломано. Я бы вместо проверки "ничего не изменилось" повесил какой-то детерминированный сигнал, хоть счетчик примененных страниц.

  2. В loadAllOperations сортировка проверяется внутри страницы, но не на стыке. Если между последним элементом страницы N и первым N+1 инверсия без дубля, тест это не увидит. При том что весь следующий раздел как раз про нестабильный порядок. Лечится одним ассертом в цикле.

  3. В чеклисте нет серверной валидации курсора. Вы цитируете AIP-158, где сказано отвечать ошибкой при смене параметров, но теста на 400 нет ни одного. А ведь инцидент из начала статьи (status=FAILED с чужим курсором) нормальный API просто бы отклонил, и все фронтовые защиты не понадобились бы.

  4. Ну и про точность курсора ничего нет. В постгресе timestamptz с микросекундами, в JS дата с миллисекундами. Строгое < по округленному значению это готовый пропуск или дубль ровно на границе страницы. Тут напрашивается юнит-тест на encode/decode курсора туда-обратно, а такого уровня в вашей таблице вообще нет.

Тема нужная. Просто раз уж туториал, такие вещи стоило бы поправить.

Надеюсь я правильно понял вопрос! parent действительно один — он задаёт только порядок вычисления, кто раньше. А вот само условие смотрит на сколько угодно полей сразу:

<gen if="Пол.М && Страна.RU && Возраст > 60" type="text" value="льготный"/>
<gen if="Пол.Ж && Страна.DE" type="text" value="другой"/>
<gen type="text" value="базовый"/>

Ветки проверяются сверху вниз, первая подошедшая и срабатывает — так что комбинации возраст + пол + страна + дата разбираются как обычная лесенка условий. Подробности в разделе про условия, а если развилка идёт по одному полю, но с долями внутри — есть <switch> и <mix>.

А когда значение не выбирается, а считается из нескольких других — это <compute>: он читает любое количество полей и выводит из них результат.

Отдельная история — когда выбирать надо не значение, а целую запись из справочника. Для этого есть <pool>, и у него свой фильтр, тоже с составными условиями: filter="spec == Needs && room > 120" — то есть круг кандидатов сужается сразу по нескольким полям строки. Как раз про это будет следующая статья, там разберу подробно.

Дальше, чем кажется. Есть <compute> — считает из уже готовых значений, на нём держатся контрольные суммы вроде Луна и IBAN. Есть <gen type="formula"... — любые расчёты прямо в конфиге, с полусотней функций. И есть prev() — ссылка на предыдущую строку/генерацию, отсюда берутся последовательности и накопления:

<gen type="formula" expr="prev(Balance, 10000) - Amount"/>

А если своя логика всё-таки нужна — её можно вынести наружу через <gen type="http"...: ваш сервис остаётся вашим кодом, движок просто к нему обращается.

Спасибо, что поделились своим опытом!

Да, это генератор тестовых данных, всё верно. Статья первая из цикла, вводная — я в ней просто показал на простом примере, как оно работает.

Про сам инструмент написал только в конце. Побоялся, что иначе получится самореклама, а Хабр её не жалует. Похоже, перестарался: раз вопрос возник первым же.

Про валидаторы вы правы, всё так. Только валидатор ничего не создаёт — он умеет сказать «нет». А данные для тестов надо откуда-то взять, и желательно сразу непротиворечивые. Бывает нужно и обратное: намеренно сломанные, чтобы проверить сам валидатор.

А про ветвление вы прямо в точку, в статье ровно оно и есть. Ваши примеры с ИНН и ДУЛ, кстати, нагляднее моих диагнозов.

Информация

В рейтинге
646-й
Дата рождения
Зарегистрирован
Активность

Специализация

Инженер по автоматизации тестирования, Инженер по производительности
Ведущий