Слогер Создать блог
Разработка

Как отслеживать флаки-тесты: дашборд на одной таблице

CI по умолчанию выбрасывает историю, а без истории флаки не диагностируются. Достаточно одной append-only таблицы и парсера JUnit XML, чтобы увидеть, какие тесты чинить и когда что-то меняется под капотом.

CI выбрасывает историю по умолчанию. Для сборки это нормально, но для флаки-тестов — фатально: без истории нет диагностики. Одна append-only таблица и парсер для файла, который ваш CI уже пишет, — вот и весь дашборд. Он отвечает на три вопроса: какой тест чинить, день ли сегодня неудачный и не поменялась ли модель под нами.

Одна таблица — на уровне попыток

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

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

  • model_served — модель из ответа, а не из конфига. Запрошенная модель статична и бесполезна, а реальная может меняться, когда алиас двигают. Без этой колонки вы не свяжете всплеск флаков с тихим обновлением модели.
  • error_sig — нормализованная сигнатура ошибки. Считается при загрузке, а чинится потом за год истории — это боль.
  • http_status — отделяет «провайдер вернул 429» от «упал ассерт». Это разные люди решают, а без колонки они выглядят одинаково.
  • branch — флак на основной ветке и на фиче имеют разный масштаб, и в скоринге это учитывается.

Таблица должна быть append-only. Строка — исторический факт, и как только она станет изменяемой, кто-нибудь «поправит» пакет строк, и все трендовые линии тихо поедут. Ошибся парсер — удаляй строки по run_id и перечитывай из артефакта, он еще цел.

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

Загрузка из того, что CI уже производит

Не нужно инструментировать тестовый процесс. JUnit XML уже генерируют pytest (--junitxml), Vitest (--reporter=junit) и Playwright. Ретраи попадают в тот же testcase как flakyFailure или rerunFailure — ровно та структура, которая нужна таблице.

  1. Убедитесь, что каждый джоб пишет JUnit XML в фиксированное место, включая успешные прогоны. Они — знаменатель всех ставок.
  2. Добавьте всегда выполняемый шаг парсинга с метаданными CI: run_id, коммит, ветка. В GitHub Actions — if: always(), в GitLab — when: always. Иначе собираете данные только с зеленых прогонов, это бесполезно.
  3. Upsert с уникальным ключом (run_id, test_id, attempt), чтобы повторный запуск не дублировал строки.
  4. Подключите инструмент отображения: Grafana по Postgres, тетрадка с расписанием, статический HTML. Хранение — сложная часть, рендер — нет.

Три запроса, которые окупаются

Дашборд — не стена графиков. Три ответа закрывают почти все причины открыть его.

Первый — ранжированный список флаки-тестов за 30 дней. Для каждого теста считаем долю прогонов, где тест и падал, и проходил. Отсекаем те с менее чем 30 прогонами — это статистический мусор. Получаем бэклог «что чинить на этой неделе».

Второй — ежедневный флейк-рейт всего сьюта. Видно, сегодняшний день нетипичный или это новая норма. Дополнительно считаем distinct_causes: 40 падений с одной причиной — это инцидент; 4 падения с четырьмя причинами — просто неудачный день четырех несвязанных тестов.

Третий — флейк-рейт по каждой модели. Это звезда. Новое значение в model_served в тот же день, когда десяток тестов начал флакать, — готовый диагноз без чтения стектрейсов. Именно для этого стоит затевать всё. Работает, только если кто-то записывает реально обслужившую запрос модель, а не из конфига. Если сьюта переключается между провайдерами на лету, заполняйте колонку из ответа, который тест уже получил.

Хранение и стоимость

На уровне попыток строки копятся быстро: тесты × попытки × прогоны в день. Для пары сотен тестов в активном репозитории это сотни тысяч строк в месяц. Postgres такое переварит, но посчитайте заранее, чтоб не узнать на падении.

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

Что выкинуть

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

По материалам: ai. Текст переработан редакцией Слогера.

← На главную

Рекламное место — Конец поста
Реклама · Слогер

Комментарии (0)

Войдите, чтобы комментировать.

Пока нет комментариев. Будьте первым.