Flaky tests

Как думать о flaky tests системно: причины, классификация и стратегии уменьшения шума без самообмана.

Обновлено:

Содержание

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

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

Не объявляйте падение ложным заранее

Перемежающийся отказ может означать реальную race condition продукта: например, один из параллельных ответов перезаписывает актуальное состояние. Такой дефект важен для пользователя, даже если повторный запуск теста успешен. Метка flaky описывает наблюдаемую нестабильность, но не доказывает, что виноват тест.

Разделяйте причины после расследования: приложение, тестовый код, данные, инфраструктура, внешняя зависимость. Полезно оставлять категорию unknown до появления свидетельств. Преждевременный ярлык «проблема стенда» часто закрывает вопрос, который команда ещё не изучала.

Сохраните первое падение

До повторного прогона сохраните сборку, окружение, seed, входные данные, порядок и параллельность. Для UI нужны trace или screenshot, фактические запросы и состояние страницы. Для API — безопасный request, response и correlation id. Для асинхронного процесса полезна последовательность событий.

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

Системное состояние и изоляция

Общая база, аккаунт, файл или корзина создают зависимости между тестами. Один кейс меняет локаль, другой ожидает английский текст; один удаляет запись, другой считает её существующей. Такие проблемы часто проявляются только параллельно или после определённого предшественника.

Сначала запустите сценарий отдельно, затем вместе с предполагаемыми соседями и при нужной параллельности. Используйте уникальные данные и явную очистку. Если отказ исчезает только в последовательном режиме, это улика, а не достаточное основание навсегда отключить параллельность.

Асинхронность и неправильное ожидание

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

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

Практический пример: заказ исчезает из списка

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

Зафиксируйте ID созданного заказа и журнал удаления, затем повторите тесты параллельно на отдельных пользователях. Ожидается отсутствие чужого удаления. Исправление заключается в данных на сценарий и адресной очистке собственных ID. После него контрольный прогон должен включать прежнюю параллельность и достаточное число повторений.

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

Время, случайность и порядок

Тесты на дату могут зависеть от timezone, перехода через полночь и часов сервера. Задайте контролируемое время на подходящем уровне и проверьте границы отдельно. Сравнение «создано сейчас» без допустимого интервала часто неустойчиво даже при корректном продукте.

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

Как измерять проблему

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

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

Retry и карантин

Ограниченный retry помогает пережить известный временный сбой и собрать дополнительные данные. Но он должен сохранять первый результат и явную метрику нестабильности. Бесконечные повторы или автоматическое «до зелёного» уничтожают смысл проверки.

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

Порядок исправления

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

После исправления добавьте узкую регрессию на найденный механизм, если он воспроизводится на нижнем уровне. Затем повторите исходную широкую цепочку. Это помогает сохранить контроль дефекта и уменьшить будущую стоимость обнаружения.

Типичные ошибки и выводы

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

Системная работа с flaky tests состоит из свидетельств, классификации причины и проверяемого исправления. Устойчивость появляется, когда команда управляет данными, временем, зависимостями и ожиданиями, а первоначальное падение остаётся полезным сигналом даже после успешного повтора.

Что ещё почитать