После недавнего рефакторинга экрана входа локально проверили резервный сценарий с паролем, но никто не заметил, что ветка без зарегистрированной биометрии больше не позволяет продолжить работу. Проблема проявилась лишь в тестовой среде после повторного использования старого симулятора: процесс стал время от времени зависать. Причина таких сбоев не в алгоритме, а в том, что тесты не контролируют состояние аутентификации. Облачный Mac позволяет закрепить состояние симулятора, события аутентификации и очистку в конвейере, чтобы каждый коммит проходил через один и тот же набор веток.
Сначала определите границы автоматизации
simctl biometric позволяет имитировать регистрацию и отсутствие регистрации, а также события совпадения и несовпадения. Это подходит для проверки интерфейса и переходов между состояниями после получения результата приложением. Однако команда не проверяет датчик и реальный процесс регистрации, а также не подтверждает безопасность на уровне устройства. Цели тестирования следует ограничить четырьмя задачами:
| Сценарий | Заданное состояние | Ожидаемое поведение приложения |
|---|---|---|
| Успешная аутентификация | enroll + match | Переход на защищённую страницу |
| Отказ пользователя | enroll + nonmatch | Сохранение текущей страницы с возможностью повторной попытки |
| Биометрия не зарегистрирована | unenroll | Отображение доступного альтернативного сценария |
| Функция недоступна | Тестовый двойник возвращает ошибку | Отсутствие циклических диалогов и сохранение пользовательского ввода |
Тест на симуляторе доказывает, что «приложение правильно обрабатывает результат системы», а не то, что «сама биометрия надёжна».
Приёмочные проверки на реальных устройствах по-прежнему необходимы, но не нужно возлагать тестирование каждой бизнес-ветки на ручные операции.
Оберните системную аутентификацию в заменяемую границу
Если контроллер представления самостоятельно создаёт контекст аутентификации, модульному тесту остаётся только ждать системного диалога. Более надёжный подход — определить небольшой протокол: рабочая реализация вызывает системный фреймворк, а тестовая возвращает заранее заданный результат.
import LocalAuthentication
protocol BiometricAuthenticating {
func authenticate(reason: String) async throws -> Bool
}
struct SystemBiometricAuthenticator: BiometricAuthenticating {
func authenticate(reason: String) async throws -> Bool {
let context = LAContext()
var error: NSError?
guard context.canEvaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
error: &error
) else {
throw error ?? LAError(.biometryNotAvailable)
}
return try await context.evaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
localizedReason: reason
)
}
}
Бизнес-слой должен получать только BiometricAuthenticating. Модульные тесты проверяют все варианты сопоставления ошибок, а в UI-тестах остаётся небольшое количество сквозных сценариев. Они подтверждают, что после появления системной панели успешный исход, ошибка и кнопка перехода к резервному варианту действительно ведут на нужные страницы. Тогда изменения текста системных подсказок между версиями не заставят основную логику зависеть от хрупкого полного совпадения строк.
Зафиксируйте симулятор и управляйте состоянием аутентификации
Сначала проверьте параметры, поддерживаемые текущей версией инструментов, чтобы не копировать напрямую синтаксис команд из другой среды выполнения:
xcrun simctl help biometric
xcrun simctl list devices available
В конвейере не следует использовать неоднозначный селектор booted. Если параллельные задачи одновременно запускают устройства, он может выбрать не тот экземпляр. Каждая задача должна создать или получить уникальный UDID и использовать его во всех командах:
set -euo pipefail
UDID="${SIMULATOR_UDID:?missing simulator udid}"
xcrun simctl boot "$UDID" 2>/dev/null || true
xcrun simctl bootstatus "$UDID" -b
xcrun simctl biometric "$UDID" unenroll
xcrun simctl biometric "$UDID" enroll
После запуска теста событие нужно отправлять только тогда, когда панель аутентификации действительно готова. Фиксированная двухсекундная задержка легко перестаёт работать при изменении нагрузки. Можно настроить тестовую сборку так, чтобы непосредственно перед вызовом аутентификации она выводила собственный маркер, например AUTH_PROMPT_READY. Внешний контроллер отслеживает этот маркер в потоке журналов с ограничением по времени и только затем выполняет команды:
xcrun simctl biometric "$UDID" match face
xcrun simctl biometric "$UDID" nonmatch face
Допустимые параметры типа биометрии могут различаться между средами выполнения. Поэтому окончательные команды следует сверять с локальным выводом help biometric. Несовместимость должна выявляться на этапе предварительной проверки среды, а не посреди выполнения тестов.
Изолируйте тестовые сценарии друг от друга
Состояние регистрации биометрии принадлежит симулятору, а не отдельному тестовому методу. Оставшееся после предыдущего теста состояние enroll загрязнит следующий тест сценария «биометрия не зарегистрирована». Для каждого сценария рекомендуется явно задавать исходное состояние и восстанавливать его после завершения теста:
cleanup() {
xcrun simctl biometric "$UDID" unenroll 2>/dev/null || true
xcrun simctl shutdown "$UDID" 2>/dev/null || true
}
trap cleanup EXIT
Не используйте один набор устройств совместно
При параллельном запуске нескольких тестовых шардов на одном облачном Mac от LemonVM задавайте каждой задаче отдельный каталог --set или заранее выделяйте разные UDID. Наборы устройств, DerivedData и пакеты результатов также должны храниться в каталогах с идентификаторами задач, чтобы одна задача не удалила устройства другой.
Не проверяйте точный текст системных сообщений
Системные диалоги зависят от версии системы, языка и возможностей устройства. UI-тесты должны находить системную панель и собственные кнопки приложения, а затем проверять итоговое бизнес-состояние. Не следует посимвольно сравнивать системные подсказки. Точное сопоставление типов ошибок нужно проверять в модульных тестах с тестовым двойником протокола.
Собирайте данные о сбоях и используйте контрольный список допуска
При сбое необходимо сохранять как минимум пакет результатов тестирования, журналы приложения, целевой UDID, версию системы, модель симулятора и состояние регистрации перед запуском сценария. Одного снимка экрана с ошибкой обычно недостаточно, чтобы понять, было ли событие отправлено слишком рано, выбрано ли неправильное устройство или приложение не обработало обратный вызов.
Перед отправкой изменений можно использовать следующий контрольный список:
- Реализацию аутентификации можно внедрить, а бизнес-слой не зависит напрямую от системного контекста.
- Для всех четырёх сценариев — успеха, отказа, отсутствия регистрации и недоступности функции — заданы однозначные проверки.
- Каждая параллельная задача использует отдельный UDID, а глобальный селектор
bootedне применяется. - Перед отправкой события предусмотрены наблюдаемый маркер готовности и ограничение по времени.
- Каждый тест явно задаёт состояние регистрации и выполняет очистку при завершении.
- Артефакты сбоя содержат пакет результатов, журналы и сведения о симуляторе.
- В процессе выпуска сохраняется минимальная приёмочная проверка на реальном устройстве.
Ценность этой структуры не в нескольких дополнительных командах, а в превращении «состояния аутентификации» из невидимой предпосылки в тестовый ввод. Если входные данные, устройство и временные параметры можно зафиксировать, регрессионные проверки биометрии перестают быть нерегулярной ручной процедурой и становятся воспроизводимым инженерным процессом.
Часто задаваемые вопросы
Может ли simctl biometric полностью заменить проверку на реальном устройстве?
Нет. Команда проверяет ветвление приложения, диалоги и резервный вход, но не качество датчика и реальную регистрацию. Перед выпуском необходим отдельный прогон на физическом устройстве.
Почему событие match или nonmatch иногда не доходит до приложения?
Обычно событие отправляется до появления системного диалога либо несколько задач используют один симулятор. Нужно ждать наблюдаемый сигнал готовности и назначать каждой задаче отдельный UDID.
Выберите облачный Mac LemonVM под срок задачи
Lemon M4 и Lemon M4 Pro доступны в Сингапуре, Токио, Сеуле, Гонконге и на западе США с оплатой за день, неделю, месяц или квартал.