Créer des tests de régression biométrique iOS avec simctl sur un Mac distant

Security ·~6 min de lecture

Créer des tests de régression biométrique iOS avec simctl sur un Mac distant

Créer des tests de régression biométrique iOS avec simctl sur un Mac distant

La page de connexion venait d’être remaniée. Le recours au mot de passe avait bien été vérifié en local, mais personne n’avait remarqué que le parcours était désormais bloqué lorsque la biométrie n’était pas configurée. Le problème n’est apparu que dans l’environnement de test, lorsqu’un ancien simulateur a été réutilisé et que le processus s’est mis à se bloquer de manière aléatoire. Ce type d’incident ne relève pas de l’algorithme : il vient de tests qui ne maîtrisent pas l’état d’authentification. Un Mac distant permet d’intégrer l’état du simulateur, les événements d’authentification et les étapes de nettoyage dans un pipeline, afin que chaque commit couvre exactement les mêmes branches.

Définir clairement les limites de l’automatisation

simctl biometric peut simuler les états inscrit et non inscrit, ainsi que les événements de correspondance et de non-correspondance. Il convient donc pour vérifier l’interface et les transitions d’état après réception du résultat par l’application. En revanche, il ne permet ni de valider le capteur, ni de tester le véritable processus d’inscription, ni de démontrer la sécurité au niveau de l’appareil. Les tests doivent se limiter à quatre objectifs :

Scénario État injecté Comportement attendu de l’application
Authentification réussie enroll + match Afficher la page protégée
Refus de l’utilisateur enroll + nonmatch Rester sur la page actuelle et permettre une nouvelle tentative
Biométrie non configurée unenroll Afficher une solution de remplacement utilisable
Fonction indisponible Le substitut de test renvoie une erreur Ne pas afficher la boîte de dialogue en boucle et ne pas perdre les données saisies

Les tests sur simulateur démontrent que « l’application traite correctement le résultat du système », et non que « la biométrie elle-même est fiable ».

La validation sur des appareils réels reste nécessaire, mais il est inutile de faire reposer chaque branche métier sur des opérations manuelles.

Encapsuler l’authentification système derrière une interface remplaçable

Si le contrôleur de vue crée directement le contexte d’authentification, les tests unitaires ne peuvent qu’attendre l’affichage de la boîte de dialogue système. Une approche plus robuste consiste à définir un petit protocole : l’implémentation de production appelle le framework système, tandis que celle des tests renvoie un résultat déterministe.

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
        )
    }
}

La couche métier ne reçoit alors qu’un BiometricAuthenticating. Les tests unitaires couvrent exhaustivement la correspondance entre les erreurs et les comportements attendus. Les tests d’interface conservent seulement quelques scénarios de bout en bout afin de vérifier qu’après l’apparition du panneau système, les actions de réussite, d’échec et de repli conduisent bien aux pages appropriées. Ainsi, même si le texte des messages système change d’une version à l’autre, la logique centrale ne dépend pas d’une comparaison intégrale fragile.

Utiliser un simulateur déterminé et piloter son état biométrique

Commencez par vérifier les paramètres pris en charge par les outils installés, afin de ne pas recopier directement une syntaxe provenant d’un autre environnement d’exécution :

xcrun simctl help biometric
xcrun simctl list devices available

Le pipeline ne doit pas utiliser le sélecteur ambigu booted. Lorsque plusieurs tâches démarrent des appareils en parallèle, celui-ci peut cibler la mauvaise instance. Chaque tâche doit créer ou réserver un UDID unique, puis l’utiliser dans toutes les commandes :

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

Une fois le test lancé, attendez que le panneau d’authentification soit réellement prêt avant d’envoyer l’événement. Une pause fixe de deux secondes devient vite peu fiable lorsque la charge varie. La version utilisée pour les tests peut, par exemple, produire son propre marqueur tel que AUTH_PROMPT_READY juste avant de déclencher l’authentification. Le contrôleur externe surveille alors le flux de journaux avec un délai d’expiration et n’exécute les commandes suivantes qu’après avoir détecté ce marqueur :

xcrun simctl biometric "$UDID" match face
xcrun simctl biometric "$UDID" nonmatch face

Les paramètres de type biométrique acceptés peuvent varier selon l’environnement d’exécution. Les commandes définitives doivent donc se conformer à la sortie locale de help biometric. Toute incompatibilité doit être détectée pendant le contrôle préalable de l’environnement, et non au milieu des tests.

Rendre chaque scénario indépendant

L’état d’inscription biométrique appartient au simulateur, pas à une méthode de test particulière. Un enroll laissé par le scénario précédent peut contaminer le test suivant consacré à l’absence d’inscription. Il est recommandé de définir explicitement l’état initial de chaque scénario, puis de le réinitialiser à la fin du test :

cleanup() {
  xcrun simctl biometric "$UDID" unenroll 2>/dev/null || true
  xcrun simctl shutdown "$UDID" 2>/dev/null || true
}
trap cleanup EXIT

Ne pas partager le même ensemble d’appareils

Lorsque plusieurs partitions de tests s’exécutent en parallèle sur le même Mac distant LemonVM, attribuez à chaque tâche un répertoire --set distinct ou réservez à l’avance des UDID différents. L’ensemble d’appareils, DerivedData et les paquets de résultats doivent tous être répartis dans des répertoires propres à chaque identifiant de tâche, afin qu’une tâche ne puisse pas supprimer les appareils d’une autre.

Ne pas faire d’assertions sur les textes système

Les boîtes de dialogue système varient selon la version du système, la langue et les capacités de l’appareil. Les tests d’interface doivent rechercher le panneau système, les boutons propres à l’application et l’état métier final, sans comparer mot pour mot les messages système. La correspondance précise entre les types d’erreurs et les comportements attendus doit être vérifiée dans les tests unitaires utilisant le substitut du protocole.

Collecter les éléments de diagnostic et définir les critères d’admission

En cas d’échec, conservez au minimum le paquet de résultats des tests, les journaux de l’application, l’UDID ciblé, la version du système, le modèle du simulateur et l’état d’inscription avant le début du scénario. Une simple capture d’écran de l’échec ne permet généralement pas de déterminer si l’événement a été envoyé trop tôt, si le mauvais appareil a été sélectionné ou si l’application n’a pas traité le rappel.

Avant chaque intégration, utilisez la liste de contrôle suivante :

L’intérêt de cette structure n’est pas d’exécuter quelques commandes supplémentaires, mais de transformer « l’état d’authentification », auparavant implicite et invisible, en une donnée d’entrée des tests. Dès lors que les entrées, l’appareil et la chronologie peuvent être enregistrés, les contrôles de régression biométrique cessent d’être des vérifications manuelles occasionnelles pour devenir une étape d’ingénierie reproductible.

Questions fréquentes

simctl biometric remplace-t-il les tests biométriques sur appareil physique ?

Non. Il valide les transitions de l’application, les dialogues et le chemin de repli, mais pas la qualité du capteur ni l’inscription réelle. Une validation sur appareil reste nécessaire avant publication.

Pourquoi un événement match ou nonmatch n’atteint-il parfois pas le test ?

L’événement est souvent envoyé avant l’apparition du dialogue système, ou plusieurs tâches partagent le même simulateur. Attendez un signal de disponibilité observable et attribuez un UDID distinct à chaque tâche.

Nœud physique dédié

Choisissez un Mac dans le cloud LemonVM selon la durée de votre mission

Lemon M4 et Lemon M4 Pro sont disponibles à Singapour, Tokyo, Séoul, Hong Kong et dans l’ouest des États-Unis, avec une facturation à la journée, à la semaine, au mois ou au trimestre.

Louer un Mac dans le cloud