Нестабильные тесты в Elixir
- Выполните общую настройку Elixir, чтобы отправлять результаты прогонов тестов
Нестабильные тесты — это тесты, которые дают разные результаты (проходят или падают) при многократном запуске с одним и тем же кодом. Они подрывают доверие к набору тестов и отнимают время разработчиков на расследование ложных сбоев. Tuist автоматически обнаруживает нестабильные тесты и помогает отслеживать их со временем.

Как работает обнаружение нестабильности#
Tuist обнаруживает нестабильные тесты двумя способами:
Повторные запуски тестов#
ExUnit запускает каждый тест один раз, поэтому тест, который иногда падает, выглядит так же, как сломанный. При включённых повторных запусках Hex-пакет повторно выполняет упавшие тесты столько раз, сколько вы указали. Тест, который проходит при повторном запуске, помечается как нестабильный, а прогон считается успешным, если все ранее упавшие тесты в итоге проходят.
По умолчанию повторные запуски отключены. Добавьте test_retries в существующие настройки Tuist в mix.exs, чтобы включить их:
tuist: [project: "account/project", cache: true, test_retries: 2]
Или для одного прогона используйте флаг или переменную окружения:
mix test --retries 2
TUIST_TEST_RETRIES=2 mix test
Флаг имеет приоритет над переменной окружения, а переменная окружения — над mix.exs.
Повторно запускаются только упавшие тесты, как это делает mix test --failed, а на дашборде отображается каждая попытка выполнения теста и её длительность. Прогон, который ExUnit остановил досрочно из-за --max-failures, не повторяется. Прогон, у которого все упавшие тесты превысили таймаут, также не повторяется, поскольку каждый повторный запуск снова ждал бы истечения всех таймаутов. Повторные запуски также пропускаются при использовании --warnings-as-errors, которое mix test больше не применяет, и при запуске из корня umbrella-проекта; чтобы включить повторные запуски, выполняйте тесты внутри одного из приложений проекта.

Обнаружение между прогонаами#
Даже без повторных запусков тестов Tuist может обнаруживать нестабильные тесты, сравнивая результаты разных прогонов CI для одного коммита. Если тест проходит в одном прогоне CI, но падает в другом прогоне для того же коммита и схемы (--scheme), неудачное выполнение помечается как нестабильное.
Это особенно полезно для выявления нестабильных тестов, которые падают недостаточно регулярно, чтобы их поймали повторные запуски, но всё равно вызывают периодические сбои в CI.
Управление нестабильными тестами#
Автоматическая очистка#
Обнаружение и очистка выполняются через автоматизированное оповещение в проекте. Каждый проект получает автоматизацию «Обнаружение нестабильных тестов» по умолчанию, чей триггер помечает тест как нестабильный, а восстановление снимает флаг, когда тест проходит настроенный период восстановления (по умолчанию 14 дней) без повторного срабатывания. Отредактируйте её в разделе Настройки → Автоматизации, чтобы изменить период восстановления, действия при восстановлении (например, также вывести тест из карантина) или полностью отключить восстановление, чтобы тесты оставались помеченными как нестабильные, пока вы не снимете метку вручную.
Ручное управление#
Вы также можете вручную пометить или снять пометку о нестабильности тестов на странице сведений о тест-кейсе. Это полезно, когда:
- Вы хотите подтвердить наличие известного нестабильного теста во время работы над исправлением
- Тест был ошибочно помечен из-за проблем с инфраструктурой
Карантин нестабильных тестов#
Карантин не даёт нестабильному тесту блокировать CI, пока вы его исправляете. На странице теста в дашборде измените его состояние на Игнорировать сбои или Пропущен, либо настройте автоматизацию в разделе Настройки → Автоматизации, которая будет игнорировать или пропускать тест при появлении нестабильности и снова включать его после восстановления. Перед запуском тестов mix test считывает все тесты проекта, находящиеся в карантине, и применяет правила:
- Игнорировать сбои: тест выполняется, и его сбой по-прежнему фиксируется, но прогон считается успешным, если единственные сбои — это тесты с игнорированием сбоев. Повторные запуски всё равно перезапускают падающий тест с игнорированием сбоев, поэтому нестабильный тест может пройти и быть помечен как нестабильный.
- Пропущен: тест не выполняется.
mix testвыводит список пропущенных тестов перед началом прогона.
Tuist: skipping 1 quarantined test:
MyApp.CheckoutTest: create_order/2 times out
Tuist: 1 muted test run, and their failures do not fail the run.
Прогон завершается ошибкой, как обычно, если любой тест без игнорирования сбоев падает или если ExUnit останавливается досрочно из-за --max-failures.
- Карантин применяют только свежие версии Hex-пакета. Более старые версии запускают тесты с игнорированием сбоев и пропущенные тесты как обычные, поэтому сбой такого теста всё равно приводит к ошибке прогона. Обновите
tuist_exперед тем, как полагаться на эту функцию. - Игнорирование сбоев не применяется при использовании
--warnings-as-errorsили при запуске из корня umbrella-проекта. В этих случаях их сбои приводят к ошибке прогона. Вместо этого запускайте тесты внутри каждого приложения umbrella-проекта. - Пропуск сопоставляет тест по блоку describe и имени, а не по модулю. Тест с тем же блоком describe и именем в другом модуле также будет пропущен. См. как пропуск выбирает тесты.
- Пропущенный тест всё равно выполняется, если вы выбрали его через
--only,--includeилиpath:line, либо если он объявлен не черезtest, например как StreamDataproperty. - Если
mix testне может прочитать тесты в карантине, он запускает все тесты и учитывает все сбои. - Чтение тестов в карантине требует учётных данных, даже для публичного проекта. Прогон без
TUIST_TOKEN, сессииmix tuist.loginили токена идентификации непрерывной интеграции не применяет карантин. Это включает pull request'ы из форков, которые обычно выполняются без секретов. - При использовании
--coverпропущенный тест считается удалённым: строки, которые покрывал только он, отображаются как непокрытые.
mix test после сводки перечисляет тесты, которые были ошибочно исключены пропуском, а также пропущенные тесты, которые всё равно выполнились.
Как пропуск выбирает тесты#
Пропущенный тест исключается фильтром ExUnit по полному имени, например --exclude "test:test create_order/2 times out". Фильтры ExUnit не могут дополнительно учитывать модуль, поэтому:
- Тест с тем же блоком describe и именем в другом модуле также исключается.
mix testперечисляет такие тесты после прогона. - Тест, выбранный через
--only,--includeилиpath:line, выполняется, даже если он пропущен, потому что ExUnit применяет фильтры включения после фильтров исключения. То же самое относится к тестам, которые не объявлены черезtest, например StreamDataproperty.mix testперечисляет такие тесты после прогона.
Повторный запуск тестов в карантине#
Передайте --no-quarantine, чтобы запустить все тесты и учесть все сбои, например для проверки исправления:
mix test --no-quarantine
Верните тесту состояние Включён на его странице, чтобы учитывать его в последующих прогонах. Пропущенный тест не даёт новых результатов, пока не будет запущен снова.
Если mix test не может прочитать тесты в карантине, он выводит предупреждение и запускает все тесты без учёта карантина. Без учётных данных или настроенного проекта он запускает все тесты без предупреждения.
Hex-пакет пока не проводит стресс-тестирование новых тестов.
Уведомления в Slack#
Получайте уведомления, когда тест становится нестабильным, настроив оповещения о нестабильных тестах в вашей интеграции со Slack.
Запрос состояния нестабильности#
Данные доступны по HTTP. См. эндпоинты тест-кейсов в справочнике API для получения полного списка маршрутов, фильтров и полей ответа. Чтобы отметить или снять отметку о нестабильности тест-кейса, отправьте PATCH /api/projects/{account_handle}/{project_handle}/tests/test-cases/{test_case_id} с параметром is_flaky или используйте инструмент протокола Model Context Protocol update_test_case.