# Сгенерированный Xcode проект {#generated-xcode-project-flaky-tests}

> [!WARNING]
> **Требования**
>
> - <.localized_link href="/guides/features/test-insights/xcode">Инсайты тестов Xcode</.localized_link> настроены (автосгенерированные схемы включают пост-действие `tuist inspect test`; в пользовательские схемы его нужно добавить).

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

![Страница нестабильных тестов](/images/guides/features/test-insights/flaky-tests-page.png)

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

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

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

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

Передайте `-retry-tests-on-failure` или `-test-iterations` через `tuist test`:

```sh
tuist test --scheme MyScheme -- -retry-tests-on-failure -test-iterations 3
```

![Детали нестабильного тест-кейса](/images/guides/features/test-insights/flaky-test-case-detail.png)

### Обнаружение между запусками {#cross-run-detection}

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

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

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

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

### Автоматическая очистка

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

### Ручное управление

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

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

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

Тест в карантине находится в одном из двух режимов:

- **Заглушен**: тест всё ещё запускается, но `tuist test` маскирует сбой. Сбои всё ещё поступают в детектор нестабильных тестов, поэтому вы можете продолжать наблюдать за тестом, не ломая сборку. Выберите этот режим для теста, который вы активно исследуете.
- **Пропущен**: xcodebuild получает `-skip-testing <identifier>`, поэтому тест никогда не запускается. Он не производит новых результатов и исчезает из дашборда нестабильных тестов, пока вы не включите его снова. Выберите этот режим, когда тест сломан, медленный или настолько постоянно нестабилен, что его запуск просто тратит минуты CI впустую.

### Почему тесты в карантине могут отображаться как прошедшие {#quarantined-passing}

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

### Запуск тестов {#running-tests}

`tuist test` автоматически учитывает оба режима:

```sh
tuist test
```

Пропущенные тесты передаются в xcodebuild как `-skip-testing` и никогда не запускаются. Заглушенные тесты запускаются нормально; если они падают, сбой маскируется в результирующем статусе сборки.

Чтобы полностью обойти карантин и запустить всё, включая заглушенные и пропущенные тесты:

```sh
tuist test --skip-quarantine
```

## Стресс-тестирование новых тестов {#stress-testing-new-tests}

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

Тест-кейс считается новым, если он не запускался в CI за последние 90 дней на основной ветке проекта или на коммите, который впоследствии попал в неё. При наличии очереди слияния тест-кейс, запущенный очередью, становится известным, когда коммит очереди попадает в ветку, и остаётся новым, если очередь его сбрасывает. Коммиты сопоставляются через <.localized_link href="/guides/features/test-insights/xcode#git-history">историю Git</.localized_link>, которую Tuist собирает в CI.

### Включение

Гейт по умолчанию отключен. Установите режим:

- **`report`**: предупреждает о каждом нестабильном тест-кейсе. Запуск всё ещё завершается со своим собственным результатом.
- **`enforce`**: нестабильный тест-кейс приводит к провалу запуска с тем же кодом выхода, что и упавший тест.

В обоих режимах <.localized_link href="/guides/features/selective-testing/generated-xcode-project">селективное тестирование</.localized_link> не пропускает таргет теста с новым нестабильным тест-кейсом при следующем запуске. Таргет запускается снова, и гейт повторно проводит стресс-тестирование тест-кейса, пока он не пройдет все повторные запуски.

Начните с `report` на пару недель, чтобы увидеть, что бы заблокировал `enforce`.

Передайте его перед аргументами сквозной передачи или установите `TUIST_TEST_STRESS_NEW_TESTS`, чтобы менять режим для каждого канала CI:

```sh
tuist test --stress-new-tests report
```

### Сколько раз каждый тест перезапускается

Это зависит от того, сколько времени занял тест-кейс при первом проходе:

| Первый проход | Повторные запуски |
| --- | --- |
| До 5 с | 10 |
| До 10 с | 5 |
| До 30 с | 3 |
| До 5 мин | 2 |
| Более 5 мин | Не перезапускается |

Повторные запуски используют результаты сборки первого прохода. Проход останавливается после 200 тест-кейсов или 10 минут — в зависимости от того, что наступит раньше, и сообщает, какой лимит был достигнут.

### Когда гейт не запускается

- Первый проход уже завершился неудачей. Сначала исправьте эти тесты.
- У проекта нет основной ветки, или в ней еще ничего не запускалось в CI.
- Более 30% тест-кейсов проекта выглядят как новые. Повторный запуск такого количества займет больше времени, чем это оправдано. Этого стоит ожидать в проекте, который только начал отправлять отчеты в Tuist, или после переименования, которое перемещает много тестов сразу.

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

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

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

### CLI

Дерево команд `tuist test case` предоставляет всю информацию, которую Tuist отслеживает о тест-кейсе:

```sh
tuist test case list --flaky                       # only flaky test cases
tuist test case list --quarantined                 # only muted or skipped test cases
tuist test case show <test_case_id>                # detail: flakiness rate, last status, run counts
tuist test case events <test_case_id>              # audit log: marked_flaky, muted, skipped, ...
tuist test case run list <test_case_id>            # run history with status and duration
tuist test case run show <test_case_run_id>        # single run, including failure breakdown
```

Все они принимают `--json` для скриптинга.

### REST API

Те же данные доступны по 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` или `state`. То же изменение доступно через `tuist test case update` и как инструмент Model Context Protocol `update_test_case`.
