Контроль согласованности планов тестирования Xcode

Контроль согласованности планов тестирования Xcode

После разделения модульных, интеграционных и UI-тестов между несколькими планами Xcode Test Plan команда чаще всего сталкивается не с падением тестов, а с тем, что они вообще выполняются не так, как ожидалось. Кто-то меняет план по умолчанию в локальной Scheme, кто-то временно пропускает тест и коммитит файл, а кто-то обновляет переменные окружения, не синхронизируя изменения с конвейером. Облачный Mac точно выполняет конфигурацию из репозитория, поэтому начинать следует не с дополнительных перезапусков, а с проверки самого плана тестирования как обычного кода.

Зафиксируйте единую отслеживаемую точку входа

Scheme должна быть общим файлом. Обычно он находится по пути App.xcodeproj/xcshareddata/xcschemes/App-CI.xcscheme. Не полагайтесь на xcuserdata в пользовательском каталоге: находящиеся там настройки не будут стабильно попадать в систему контроля версий.

Укажите хранящийся в репозитории файл App-CI.xctestplan в разделе Test Action для Scheme, а в конвейере явно передавайте имя плана. Перед началом работы сборочного узла убедитесь, что план доступен:

set -euo pipefail

xcodebuild \
  -workspace App.xcworkspace \
  -scheme App-CI \
  -showTestPlans

xcodebuild \
  -workspace App.xcworkspace \
  -scheme App-CI \
  -testPlan App-CI \
  -destination 'platform=iOS Simulator,name=iPhone 16' \
  test

Параметр -testPlan нельзя опускать. Если значение Scheme по умолчанию изменится, команда по-прежнему может завершиться успешно, хотя фактически будет запущен уже другой набор тестов.

Превратите файл плана в проверяемый контракт

.xctestplan — это JSON-файл, поэтому он хорошо подходит для статической проверки. Контроль должен охватывать как минимум четыре группы полей:

Объект проверки Требование Что означает ошибка
configurations Должна присутствовать конфигурация CI Точка входа конвейера удалена или переименована
testTargets Должны присутствовать согласованные цели Одна из групп тестов не выполняется
skippedTests Допустимы только элементы из списка разрешений Добавлены пропущенные тесты без объяснения
environmentVariableEntries Секретные значения запрещены, обязательные ключи должны присутствовать Окружение изменилось либо конфиденциальные данные попали в репозиторий

Не сравнивайте текст всего JSON-файла с учётом порядка элементов. Xcode может переставлять элементы массивов или добавлять поля, а посимвольное сравнение будет вызывать бессмысленные ошибки. Вместо этого разбирайте структуру и проверяйте только те ограничения, от которых действительно зависит команда.

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

Выявляйте пропущенные цели и тесты с помощью скрипта

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

#!/usr/bin/env python3
import json
import sys
from pathlib import Path

plan = json.loads(Path("App-CI.xctestplan").read_text())
required_targets = {"AppTests", "AppIntegrationTests"}
allowed_skips = {
    "AppIntegrationTests/testTemporaryServerResponse"
}

config_names = {item["name"] for item in plan.get("configurations", [])}
if "CI" not in config_names:
    sys.exit("Missing CI test configuration")

targets = plan.get("testTargets", [])
target_names = {
    item.get("target", {}).get("name")
    for item in targets
}
missing = required_targets - target_names
if missing:
    sys.exit(f"Missing test targets: {sorted(missing)}")

actual_skips = {
    test
    for item in targets
    for test in item.get("skippedTests", [])
}
unexpected = actual_skips - allowed_skips
if unexpected:
    sys.exit(f"Unapproved skipped tests: {sorted(unexpected)}")

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

Задайте условия удаления элементов из списка разрешений

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

Отделите стабильные переменные от секретов конвейера

В плане тестирования удобно хранить параметры, которые не меняются между узлами, например UITEST_MODE=1, фиксированный язык или режим имитации сервисов. Токены доступа, закрытые ключи и одноразовые учётные данные нельзя записывать в файл плана или передавать в виде открытых параметров Scheme.

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

Учитывайте также цель, в контексте которой раскрываются переменные. Если план ссылается на переименованный Target, файл может по-прежнему открываться в интерфейсе Xcode, но во время выполнения переменные будут разрешаться не так, как ожидалось. При переименовании проекта проверку плана следует запускать вместе с xcodebuild -list.

Сохраняйте достаточно данных для анализа ошибок

После успешного прохождения проверки запустите полный набор тестов и сохраните пакет результатов в фиксированном каталоге:

rm -rf artifacts/App-CI.xcresult

xcodebuild \
  -workspace App.xcworkspace \
  -scheme App-CI \
  -testPlan App-CI \
  -destination 'platform=iOS Simulator,name=iPhone 16' \
  -resultBundlePath artifacts/App-CI.xcresult \
  test

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

При работе фиксированных тестовых узлов на ZoneMini статическую проверку также следует повторять перед каждым заданием, а не считать, что длительно работающая среда неизбежно остаётся неизменной. Общая Scheme фиксирует точку входа, план тестирования описывает набор тестов, аудит-скрипт контролирует изменения, а пакет результатов сохраняет доказательства. Только сочетание всех четырёх уровней придаёт утверждению «тесты пройдены» стабильный смысл.

Часто задаваемые вопросы

Почему недостаточно зафиксировать команду xcodebuild в CI?

Команда фиксирует имя Scheme и плана, но содержимое .xctestplan может измениться. Отдельная проверка обнаруживает удалённые цели, новые исключения и изменение переменных окружения.

Нужно ли полностью запрещать skippedTests?

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

Как гарантировать выбор одного плана на рабочем Mac и в CI?

Храните Scheme в xcshareddata/xcschemes, добавьте .xctestplan в репозиторий и всегда передавайте его имя через -testPlan, предварительно проверяя список командой -showTestPlans.

ВЫДЕЛЕННЫЙ СЛОТ ДЛЯ СБОРКИ

Выберите выделенный облачный Mac для непрерывных задач сборки

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

Выбрать конфигурацию и заказать