Guide d’ingénierie MiniRent

Tests reproductibles des autorisations iOS avec simctl privacy

Tests reproductibles des autorisations iOS avec simctl privacy

Une même suite de tests d’interface iOS peut réussir sur la machine de développement, puis rester occasionnellement bloquée sur une fenêtre d’autorisation après son transfert vers un Mac dans le cloud. En général, il ne s’agit pas d’une défaillance aléatoire du code métier, mais d’un état d’autorisation TCC laissé dans le simulateur par la tâche précédente. Si quelqu’un a déjà choisi « Autoriser », les tests suivants ne verront plus la première demande. Si le choix a été « Refuser », les parcours qui dépendent des contacts ou des photos passeront directement dans la branche d’échec. Pour stabiliser les tests, il ne faut pas multiplier les nouvelles tentatives, mais définir explicitement les autorisations requises avant chaque cas de test.

Traiter l’état des autorisations comme une donnée d’entrée du test

Les tests doivent couvrir au moins trois états : aucune décision, accès accordé et accès refusé. Chacun correspond à un parcours produit distinct ; une simple étape de « réinstallation de l’application » ne permet pas de les traiter correctement.

État cible Action simctl Comportement à vérifier
Aucune décision reset L’application effectue la demande et le système affiche la fenêtre d’autorisation
Accès accordé grant La fonctionnalité continue directement, sans nouvelle demande
Accès refusé revoke L’application explique le fonctionnement dégradé ou guide l’utilisateur vers les réglages

Commencez par vérifier que le runtime prend en charge le service visé. Les services contrôlables peuvent varier selon la version du système : ne définissez donc pas leur nom de mémoire avant d’envoyer directement la modification.

xcrun simctl privacy help
xcrun simctl list devices available

Les services courants comprennent contacts, photos, photos-add, location, microphone et calendar. Le script de test doit se conformer à l’aide de la version de simctl fournie avec le Xcode utilisé.

L’état des autorisations appartient à l’appareil simulé, pas au dépôt de code. Nettoyer le répertoire de build ne supprime pas les données TCC. De même, supprimer puis réinstaller l’application ne constitue pas une méthode fiable de réinitialisation des autorisations.

Préparer un simulateur dédié et l’application

Dans la CI, évitez d’utiliser librement booted pour désigner n’importe quel appareil déjà démarré. Plusieurs tâches parallèles pourraient sélectionner le même simulateur, puis écraser mutuellement leurs installations, lancements et modifications d’autorisations. Il est plus fiable d’attribuer un UDID précis à chaque tâche et de le transmettre au script comme paramètre.

L’application doit être installée avant que grant ou revoke puisse agir sur son véritable Bundle Identifier. Ne déduisez pas non plus ce Bundle Identifier du nom du Scheme : lisez-le directement dans le produit de build.

APP_PATH="$1"
UDID="$2"
BUNDLE_ID=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" \
  "$APP_PATH/Info.plist")

xcrun simctl boot "$UDID" 2>/dev/null || :
xcrun simctl bootstatus "$UDID" -b
xcrun simctl install "$UDID" "$APP_PATH"
printf '%s
' "$BUNDLE_ID"

Si le test porte sur une extension, celle-ci possède son propre Bundle Identifier. Lisez séparément son fichier Info.plist et n’utilisez pas l’identifiant de l’application principale dans toutes les commandes d’autorisation.

Fixer les trois états initiaux avec un script

Il est plus facile de contrôler les changements d’état lorsqu’ils sont regroupés dans une fonction plutôt que dispersés dans la configuration du pipeline. Avant de modifier une autorisation, arrêtez l’application afin que son processus ne conserve pas l’ancien état ou ne soit pas en train d’afficher une fenêtre de demande.

set -euo pipefail

UDID="$1"
BUNDLE_ID="$2"
SERVICE="$3"
STATE="$4"

xcrun simctl terminate "$UDID" "$BUNDLE_ID" 2>/dev/null || :

case "$STATE" in
  undecided)
    xcrun simctl privacy "$UDID" reset "$SERVICE" "$BUNDLE_ID"
    ;;
  authorized)
    xcrun simctl privacy "$UDID" grant "$SERVICE" "$BUNDLE_ID"
    ;;
  denied)
    xcrun simctl privacy "$UDID" revoke "$SERVICE" "$BUNDLE_ID"
    ;;
  *)
    printf 'Unknown permission state: %s
' "$STATE" >&2
    exit 64
    ;;
esac

Lancez ensuite l’application ou les tests. Pour un scénario où l’accès aux contacts est accordé, vous pouvez par exemple appeler d’abord le script, puis n’exécuter que la classe de test correspondante :

./set-permission.sh "$UDID" "$BUNDLE_ID" contacts authorized

xcodebuild test \
  -scheme PermissionTests \
  -destination "platform=iOS Simulator,id=$UDID" \
  -only-testing:PermissionUITests/ContactsAuthorizedTests \
  -resultBundlePath Artifacts/ContactsAuthorized.xcresult

reset rétablit l’état dans lequel aucune décision n’a été prise ; il ne signifie pas que l’accès est refusé. Si le code métier traite ces deux états de la même manière, le test doit mettre directement en évidence ce défaut de conception.

Séparer les tests de la fenêtre système et des branches métier

La fenêtre d’autorisation appartient à l’interface système. Le repérage de ses boutons dépend donc facilement de la langue, de la version du runtime et de l’ordre des demandes. Tous les tests d’autorisations ne doivent pas reposer sur des clics dans cette fenêtre. Une meilleure séparation consiste à utiliser grant et revoke dans la majorité des tests pour vérifier les branches métier, tout en ne conservant que quelques cas consacrés à la première demande.

Cas de la première demande

Exécutez d’abord reset, puis lancez l’application et déclenchez la demande d’autorisation. Le test doit uniquement vérifier que la fenêtre apparaît et que l’application atteint l’écran attendu après le choix de l’utilisateur. Si une interaction avec l’interface système est nécessaire, utilisez un objet distinct représentant l’application système et évitez les coordonnées fixes.

Cas où l’accès est accordé ou refusé

Dans ces deux cas, aucune fenêtre d’autorisation ne doit apparaître. Lorsque l’accès est accordé, vérifiez que la fonctionnalité lit correctement les données. Lorsqu’il est refusé, vérifiez que l’application ne redemande pas l’autorisation en boucle et ne reste pas bloquée sur un chargement infini. Si un guidage vers les réglages est prévu, le test automatisé doit uniquement contrôler le bouton et le texte explicatif, sans modifier réellement les réglages système.

Pour la localisation, il faut également distinguer les implications métier d’un accès limité à l’utilisation de l’application et d’un accès permanent. simctl privacy permet de préparer un état de base, mais ne remplace pas tous les comportements d’un appareil réel. Les conclusions correspondantes doivent donc être validées sur un appareil physique.

Isolation, collecte des preuves et diagnostic des échecs

À la fin de chaque classe de test, vous pouvez exécuter reset pour le service concerné. Il est toutefois plus important que le test suivant établisse lui-même son état, sans dépendre de la réussite du nettoyage précédent. En exécution parallèle, un simulateur ne doit être attribué qu’à une seule tâche. Pour exécuter plusieurs tâches simultanément, créez plusieurs appareils au lieu de partager un UDID.

En cas d’échec, conservez au minimum le fichier .xcresult, les journaux de test, les journaux système du simulateur et la liste actuelle des appareils. Si l’état attendu n’a pas été appliqué, contrôlez les points suivants dans cet ordre :

  1. Le Bundle Identifier provient-il bien de l’application ou de l’extension réellement installée ?
  2. Le nom du service est-il pris en charge par le runtime actuel ?
  3. L’application a-t-elle été arrêtée avant la modification de l’état ?
  4. L’UDID cible correspond-il à l’appareil utilisé par xcodebuild ?
  5. Une autre tâche parallèle agit-elle sur le même simulateur ?
  6. Le code de test met-il en cache un ancien résultat d’autorisation ?

Ne faites pas de l’effacement complet du simulateur la solution par défaut. Cette opération allonge sensiblement la durée des tâches et peut masquer des défauts dans la gestion des états. Ne recréez l’appareil que si son état système reste inexplicable, en conservant au préalable les journaux disponibles.

Lors de l’exécution de ces tâches sur les Mac dans le cloud de MiniRent, le modèle et le nœud influencent uniquement le placement de la tâche. Le script de test doit toujours décrire complètement l’environnement à l’aide de l’UDID, du Bundle Identifier, du nom du service et de l’état cible, tandis que les configurations actuellement disponibles doivent être vérifiées dans la console. La même suite de régression des autorisations peut ainsi être reproduite dans une tâche de débogage temporaire comme intégrée de manière stable à l’intégration continue.

Questions fréquentes

simctl privacy remplace-t-il tous les tests de la fenêtre système ?

Non. Il prépare des états reproductibles, mais un petit ensemble de tests UI doit encore vérifier le texte, les boutons et le retour depuis la véritable fenêtre système.

Faut-il effacer le simulateur avant chaque test d’autorisation ?

En général, non. Arrêtez l’application puis exécutez reset, grant ou revoke pour le service concerné et son identifiant de bundle.

Appareil physique dédié

Utilisez un Mac mini dans le cloud pour votre prochain développement ou build

Choisissez parmi deux configurations M4, quatre durées de location et cinq nœuds disponibles à la vente. La disponibilité réelle est indiquée en temps réel dans la console.

Choisir une configuration et louer