ログイン画面をリファクタリングし、パスワードへのフォールバックはローカルで検証したものの、「生体認証が未登録」の分岐から先へ進めなくなっていることには誰も気づきませんでした。テスト環境で古いSimulatorを再利用したとき、初めてフローが不規則に停止するようになりました。この種の問題はアルゴリズムではなく、テストで認証状態を制御していないことにあります。クラウドMacを使えば、Simulatorの状態、認証イベント、クリーンアップ手順をパイプラインとして固定し、すべてのコミットで同じ分岐を検証できます。
自動化の範囲を先に明確にする
simctl biometric では、登録済み、未登録、一致、不一致の各イベントをシミュレートできます。アプリが結果を受け取った後の画面表示や状態遷移を確認するのに適しています。一方で、センサーや実際の登録手順を検証したり、デバイスレベルの安全性を証明したりすることはできません。テスト対象は次の4点に限定します。
| シナリオ | 注入する状態 | アプリの期待動作 |
|---|---|---|
| 認証成功 | enroll + match | 保護された画面へ進む |
| ユーザーによる拒否 | enroll + nonmatch | 現在の画面を維持し、再試行できる |
| 未登録 | unenroll | 実行可能な代替手段を表示する |
| 機能を利用できない | テストダブルがエラーを返す | ダイアログを繰り返し表示せず、ユーザー入力を失わない |
Simulatorテストで証明できるのは「アプリがシステムの結果を正しく処理すること」であり、「生体情報そのものの信頼性」ではありません。
実機での受け入れテストは引き続き必要ですが、すべてのビジネスロジックの分岐を手動操作だけで検証する必要はありません。
システム認証を差し替え可能な境界としてラップする
ビューコントローラーが認証コンテキストを直接生成すると、単体テストでもシステムダイアログを待つしかありません。より堅牢なのは、小さなプロトコルを定義し、本番実装ではシステムフレームワークを呼び出し、テスト実装では決まった結果を返す方法です。
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テストには少数のエンドツーエンドケースだけを残します。システムパネルが表示された後、成功、失敗、フォールバックの各ボタンが正しい画面につながることを確認します。これにより、システムの案内文がバージョンによって変わっても、主要な判定が壊れやすい全文一致に依存しません。
Simulatorを固定して認証状態を操作する
別の実行環境で使ったコマンド形式をそのままコピーしないよう、まず現在のツールが対応している引数を確認します。
xcrun simctl help biometric
xcrun simctl list devices available
パイプラインでは曖昧な booted セレクターを使用しないでください。複数のジョブが同時にデバイスを起動すると、別のインスタンスが選ばれる可能性があります。各ジョブで一意のUDIDを作成または割り当て、すべてのコマンドで同じ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
テストを開始した後は、認証パネルの準備が実際に完了してからイベントを送信する必要があります。固定で2秒待機する方法は、負荷が変動すると簡単に失敗します。テスト用ビルドから、認証を開始する直前に AUTH_PROMPT_READY などの独自マーカーを出力させます。外側のコントローラーは、タイムアウト付きのログストリームでそのマーカーを検出してから、次のコマンドを実行します。
xcrun simctl biometric "$UDID" match face
xcrun simctl biometric "$UDID" nonmatch face
実行環境によって、受け付ける生体認証タイプの引数が異なる場合があります。そのため、最終的なコマンドは対象環境の help biometric の出力に従い、テストの途中で非互換性が判明するのではなく、環境の事前チェック段階で失敗させるべきです。
テストケースを相互に独立させる
生体認証の登録状態はSimulatorに属するものであり、個々のテストメソッドに属するものではありません。前のテストケースが残した enroll によって、後続の「未登録」テストが汚染されます。各シナリオで事前状態を明示的に設定し、テスト終了時に元へ戻すことを推奨します。
cleanup() {
xcrun simctl biometric "$UDID" unenroll 2>/dev/null || true
xcrun simctl shutdown "$UDID" 2>/dev/null || true
}
trap cleanup EXIT
同じデバイスセットを共有しない
同じLemonVMクラウドMac上で複数のテストシャードを並列実行する場合は、ジョブごとに個別の --set ディレクトリを指定するか、異なるUDIDを事前に割り当てます。デバイスセット、DerivedData、結果バンドルはすべてジョブIDごとにディレクトリを分け、あるジョブが別のジョブのデバイスを消去しないようにします。
システムの文言をアサートしない
システムダイアログは、OSのバージョン、言語、デバイスの機能によって変わります。UIテストではシステムパネル、アプリ固有のボタン、最終的なビジネス状態を確認し、システムメッセージを一字一句アサートしないでください。エラー種別の厳密なマッピングは、プロトコルのテストダブルを使った単体テストで検証します。
障害解析用の証跡と受け入れチェックリストを整備する
失敗時には、少なくともテスト結果バンドル、アプリログ、対象UDID、システムバージョン、Simulatorモデル、テスト開始前の登録状態を保存します。失敗画面のスクリーンショットが1枚だけでは、イベントの送信が早すぎたのか、誤ったデバイスを選択したのか、アプリがコールバックを処理しなかったのかを判断できないことがよくあります。
コミット前には、次のチェックリストで受け入れ確認を行えます。
- 認証実装を注入でき、ビジネス層がシステムコンテキストへ直接依存していない。
- 成功、拒否、未登録、利用不可の4つの経路に明確なアサーションがある。
- 各並列ジョブが専用のUDIDを持ち、グローバルな
bootedを使用していない。 - イベント送信前に、観測可能な準備完了マーカーとタイムアウトがある。
- 各テストケースが登録状態を明示的に設定し、終了時にクリーンアップを実行する。
- 失敗時の成果物に、結果バンドル、ログ、Simulator情報が含まれている。
- リリースプロセスに、実機での最小限の受け入れテストが引き続き含まれている。
この構成の価値は、単にコマンドの実行回数を増やすことではありません。「認証状態」という見えない前提を、テスト入力へ変換できる点にあります。入力、デバイス、タイミングを記録できれば、生体認証の回帰確認を不定期な手動チェックから再現可能なエンジニアリング工程へ移行できます。
よくある質問
simctl biometricだけで実機の生体認証テストを置き換えられますか?
置き換えられません。アプリ側の分岐、表示、代替導線の検証には有効ですが、センサーや実際の登録処理は確認できないため、公開前には実機テストが必要です。
matchイベントがテストに届かないのはなぜですか?
認証パネルの表示前にイベントを送った場合や、複数ジョブが同じSimulatorを共有した場合に起きます。就緒マーカーを待ち、ジョブごとに専用UDIDを割り当ててください。
用途に合わせてLemonVMクラウドMacを選ぶ
Lemon M4とLemon M4 Proは東京、シンガポール、ソウル、香港、米国西部に対応し、日・週・月・四半期単位で利用できます。