Как тестировать регрессии биометрии iOS через simctl на облачном Mac

Security ·~5 мин чтения

Как тестировать регрессии биометрии iOS через simctl на облачном Mac

После недавнего рефакторинга экрана входа локально проверили резервный сценарий с паролем, но никто не заметил, что ветка без зарегистрированной биометрии больше не позволяет продолжить работу. Проблема проявилась лишь в тестовой среде после повторного использования старого симулятора: процесс стал время от времени зависать. Причина таких сбоев не в алгоритме, а в том, что тесты не контролируют состояние аутентификации. Облачный 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, версию системы, модель симулятора и состояние регистрации перед запуском сценария. Одного снимка экрана с ошибкой обычно недостаточно, чтобы понять, было ли событие отправлено слишком рано, выбрано ли неправильное устройство или приложение не обработало обратный вызов.

Перед отправкой изменений можно использовать следующий контрольный список:

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

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

Может ли simctl biometric полностью заменить проверку на реальном устройстве?

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

Почему событие match или nonmatch иногда не доходит до приложения?

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

Выделенный физический узел

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

Lemon M4 и Lemon M4 Pro доступны в Сингапуре, Токио, Сеуле, Гонконге и на западе США с оплатой за день, неделю, месяц или квартал.

Арендовать облачный Mac