Перейти к содержимому

Нестабильные тесты Xcode

Требования

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

Страница «Нестабильные тесты»

Как работает обнаружение нестабильности#

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

Повторные попытки тестов#

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

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

bash
tuist xcodebuild test \
-scheme MyScheme \
-retry-tests-on-failure \
-test-iterations 3

Детали нестабильного тест-кейса

Обнаружение между запусками#

Даже без повторных попыток тестов Tuist может обнаружить нестабильные тесты, сравнивая результаты разных запусков CI для одного коммита, схемы и указанного назначения (имя устройства, платформа и версия ОС). Если тест проходит в одном подходящем запуске CI, но падает в другом, неудачное выполнение помечается как нестабильное. Это работает независимо от того, какой результат пришел первым.

Один и тот же тест может стабильно вести себя по-разному в разных схемах или назначениях. Например, прохождение на iOS 26 и падение на iOS 27 не означает нестабильность. Результаты из разных схем, устройств, платформ или версий ОС не помечают друг друга как нестабильные.

Результаты без назначаемого назначения сравниваются с другими неназначенными результатами отдельно от результатов с известным назначением. Сюда входят отчеты с отсутствующими или неполными метаданными назначения и отчеты, содержащие несколько различных назначений, поскольку отдельные исходы тестов пока нельзя отнести к конкретному назначению. Различия между средами в этой группе все еще могут приводить к ложной классификации нестабильности. Обнаружение на основе повторных попыток продолжает работать. Существующие исторические классификации нестабильности остаются без изменений.

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

Управление нестабильными тестами#

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

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

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

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

  • Вы хотите подтвердить известный нестабильный тест, работая над исправлением
  • Тест был ошибочно помечен из-за проблем с инфраструктурой

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

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

По умолчанию восстановление ждет, пока условие триггера перестанет выполняться, поэтому тест остается в карантине, пока его старые нестабильные запуски не выйдут за пределы окна триггера. Чтобы восстановиться быстрее, установите Восстанавливать, когда в значение Нет новых нестабильных запусков: тест восстановится, когда окно восстановления (дни или запуски) пройдет без нового нестабильного запуска, каждый новый нестабильный запуск перезапускает это ожидание, а после восстановления только новый нестабильный запуск снова поместит его в карантин.

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

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

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

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

Запуск тестов#

tuist xcodebuild test — это сквозная обертка, которая автоматически учитывает оба режима. Используйте её так же, как вызывали бы xcodebuild:

bash
tuist xcodebuild test -scheme MyScheme

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

Обход карантина#

tuist xcodebuild test принимает --skip-quarantine для запуска всего, включая тесты с игнорированием сбоев и пропущенные тесты:

bash
tuist xcodebuild test --skip-quarantine -scheme MyScheme

Прямой вызов xcodebuild#

Если вы не можете использовать tuist xcodebuild test, самостоятельно разверните тесты в карантине в аргументы -skip-testing с помощью tuist test case list:

bash
xcodebuild test \
-scheme MyScheme \
$(tuist test case list --quarantined --skip-testing)

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

Стресс-тестирование новых тестов#

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

Тест-кейс считается новым, если он не запускался в CI за последние 90 дней на основной ветке проекта или на коммите, который впоследствии был в неё включён. При использовании очереди слияния тест-кейс, запущенный очередью, становится известным после включения коммита очереди, и остаётся новым, если очередь его отбрасывает. Коммиты сопоставляются через историю Git, которую Tuist собирает в CI.

Включение функции#

По умолчанию шлюз отключен. Выберите режим:

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

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

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

bash
tuist xcodebuild test --stress-new-tests report -scheme MyScheme

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

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

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

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

Когда шлюз не запускается#

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

Уведомления в Slack#

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

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

CLI#

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

bash
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 — см. эндпоинты тест-кейсов в справочнике 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.