# Нестабильные тесты в Elixir {#elixir-flaky-tests}

> [!IMPORTANT]
> **Требования**
>
> - Выполните <.localized_link href="/guides/get-started/elixir-project">общую настройку Elixir</.localized_link>, чтобы отправлять результаты прогонов тестов

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


![Нестабильные тесты приложения Phoenix](/images/guides/features/elixir/flaky-tests.png)

## Как работает обнаружение нестабильности {#how-it-works}

Tuist обнаруживает нестабильные тесты двумя способами:

### Повторные запуски тестов {#test-retries}

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

По умолчанию повторные запуски отключены. Добавьте `test_retries` в существующие настройки Tuist в `mix.exs`, чтобы включить их:

```elixir
tuist: [project: "account/project", cache: true, test_retries: 2]
```

Или для одного прогона используйте флаг или переменную окружения:

```bash
mix test --retries 2
TUIST_TEST_RETRIES=2 mix test
```

Флаг имеет приоритет над переменной окружения, а переменная окружения — над `mix.exs`.

Повторно запускаются только упавшие тесты, как это делает `mix test --failed`, а на дашборде отображается каждая попытка выполнения теста и её длительность. Прогон, который ExUnit остановил досрочно из-за `--max-failures`, не повторяется. Прогон, у которого все упавшие тесты превысили таймаут, также не повторяется, поскольку каждый повторный запуск снова ждал бы истечения всех таймаутов. Повторные запуски также пропускаются при использовании `--warnings-as-errors`, которое `mix test` больше не применяет, и при запуске из корня umbrella-проекта; чтобы включить повторные запуски, выполняйте тесты внутри одного из приложений проекта.

![Нестабильный тест ExUnit и его история](/images/guides/features/elixir/flaky-test-case.png)

### Обнаружение между прогонаами {#cross-run-detection}

Даже без повторных запусков тестов Tuist может обнаруживать нестабильные тесты, сравнивая результаты разных прогонов CI для одного коммита. Если тест проходит в одном прогоне CI, но падает в другом прогоне для того же коммита и схемы (`--scheme`), неудачное выполнение помечается как нестабильное.

Это особенно полезно для выявления нестабильных тестов, которые падают недостаточно регулярно, чтобы их поймали повторные запуски, но всё равно вызывают периодические сбои в CI.

## Управление нестабильными тестами {#managing-flaky-tests}

### Автоматическая очистка {#automatic-clearing}

Обнаружение и очистка выполняются через **автоматизированное оповещение** в проекте. Каждый проект получает автоматизацию «Обнаружение нестабильных тестов» по умолчанию, чей *триггер* помечает тест как нестабильный, а *восстановление* снимает флаг, когда тест проходит настроенный период восстановления (по умолчанию **14 дней**) без повторного срабатывания. Отредактируйте её в разделе **Настройки → Автоматизации**, чтобы изменить период восстановления, действия при восстановлении (например, также вывести тест из карантина) или полностью отключить восстановление, чтобы тесты оставались помеченными как нестабильные, пока вы не снимете метку вручную.

### Ручное управление {#manual-management}

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


## Карантин нестабильных тестов {#quarantining}

Карантин не даёт нестабильному тесту блокировать CI, пока вы его исправляете. На странице теста в дашборде измените его состояние на **Игнорировать сбои** или **Пропущен**, либо настройте автоматизацию в разделе **Настройки → Автоматизации**, которая будет игнорировать или пропускать тест при появлении нестабильности и снова включать его после восстановления. Перед запуском тестов `mix test` считывает все тесты проекта, находящиеся в карантине, и применяет правила:

- **Игнорировать сбои**: тест выполняется, и его сбой по-прежнему фиксируется, но прогон считается успешным, если единственные сбои — это тесты с игнорированием сбоев. Повторные запуски всё равно перезапускают падающий тест с игнорированием сбоев, поэтому нестабильный тест может пройти и быть помечен как нестабильный.
- **Пропущен**: тест не выполняется. `mix test` выводит список пропущенных тестов перед началом прогона.

```text
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`.

> [!WARNING]
> **Ограничения**
>
> - Карантин применяют только свежие версии Hex-пакета. Более старые версии запускают тесты с игнорированием сбоев и пропущенные тесты как обычные, поэтому сбой такого теста всё равно приводит к ошибке прогона. Обновите `tuist_ex` перед тем, как полагаться на эту функцию.
> - Игнорирование сбоев не применяется при использовании `--warnings-as-errors` или при запуске из корня umbrella-проекта. В этих случаях их сбои приводят к ошибке прогона. Вместо этого запускайте тесты внутри каждого приложения umbrella-проекта.
> - Пропуск сопоставляет тест по блоку describe и имени, а не по модулю. Тест с тем же блоком describe и именем в другом модуле также будет пропущен. См. [как пропуск выбирает тесты](#how-skipping-selects-tests).
> - Пропущенный тест всё равно выполняется, если вы выбрали его через `--only`, `--include` или `path:line`, либо если он объявлен не через `test`, например как StreamData `property`.
> - Если `mix test` не может прочитать тесты в карантине, он запускает все тесты и учитывает все сбои.
> - Чтение тестов в карантине требует учётных данных, даже для публичного проекта. Прогон без `TUIST_TOKEN`, сессии `mix tuist.login` или токена идентификации непрерывной интеграции не применяет карантин. Это включает pull request'ы из форков, которые обычно выполняются без секретов.
> - При использовании `--cover` пропущенный тест считается удалённым: строки, которые покрывал только он, отображаются как непокрытые.
>
> `mix test` после сводки перечисляет тесты, которые были ошибочно исключены пропуском, а также пропущенные тесты, которые всё равно выполнились.

### Как пропуск выбирает тесты {#how-skipping-selects-tests}

Пропущенный тест исключается фильтром ExUnit по полному имени, например `--exclude "test:test create_order/2 times out"`. Фильтры ExUnit не могут дополнительно учитывать модуль, поэтому:

- Тест с тем же блоком describe и именем в другом модуле также исключается. `mix test` перечисляет такие тесты после прогона.
- Тест, выбранный через `--only`, `--include` или `path:line`, выполняется, даже если он пропущен, потому что ExUnit применяет фильтры включения после фильтров исключения. То же самое относится к тестам, которые не объявлены через `test`, например StreamData `property`. `mix test` перечисляет такие тесты после прогона.

### Повторный запуск тестов в карантине {#running-quarantined-tests-again}

Передайте `--no-quarantine`, чтобы запустить все тесты и учесть все сбои, например для проверки исправления:

```bash
mix test --no-quarantine
```

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

Если `mix test` не может прочитать тесты в карантине, он выводит предупреждение и запускает все тесты без учёта карантина. Без учётных данных или настроенного проекта он запускает все тесты без предупреждения.

Hex-пакет пока не проводит стресс-тестирование новых тестов.

## Уведомления в Slack {#slack-notifications}

Получайте уведомления, когда тест становится нестабильным, настроив <.localized_link href="/guides/integrations/slack#flaky-test-alerts">оповещения о нестабильных тестах</.localized_link> в вашей интеграции со Slack.


## Запрос состояния нестабильности {#querying}

Данные доступны по HTTP. См. [эндпоинты тест-кейсов](https://tuist.dev/api/docs#tag/test-cases) в справочнике API для получения полного списка маршрутов, фильтров и полей ответа. Чтобы отметить или снять отметку о нестабильности тест-кейса, отправьте `PATCH /api/projects/{account_handle}/{project_handle}/tests/test-cases/{test_case_id}` с параметром `is_flaky` или используйте инструмент протокола Model Context Protocol `update_test_case`.
