Tester automatiquement les deep links iOS sur un Mac cloud

Tester automatiquement les deep links iOS sur un Mac cloud

Lorsqu’une application iOS confie aux deep links les retours de connexion, les pages de campagne, les détails de commande et les destinations de notification, une simple refonte du routage peut suffire à renvoyer les liens vers l’accueil ou à empiler deux fois la même page lorsque l’application est déjà ouverte. Cliquer manuellement sur quelques liens prouve seulement qu’ils fonctionnent à cet instant précis, sans couvrir le démarrage à froid, l’état au premier plan, les paramètres encodés ni les entrées non valides. Une approche plus fiable consiste à stabiliser l’environnement du simulateur sur un Mac cloud et à exposer la destination finale de chaque deep link sous une forme exploitable par l’intégration continue.

Évitez de construire des URL de façon dispersée dans les scripts de test. Commencez par tenir à jour un inventaire des routes qui précise l’entrée, la route attendue et l’état de l’application.

Scénario Entrée Résultat attendu
Détail d’un produit linklab://product/42 product/42
Paramètre de recherche linklab://search?q=swift%20ui search?query=swift ui
Identifiant manquant linklab://product/ error/invalid-product
Chemin inconnu linklab://unknown error/not-found

La valeur attendue dans cet inventaire doit correspondre à la forme canonique produite par le routeur, et non au libellé d’un bouton. Ainsi, une modification du texte de l’interface ne génère pas de faux positif, et le test peut indiquer directement si le problème concerne le chemin, les paramètres ou la restauration de l’état.

Le contrôle des deep links vérifie que « l’entrée atteint la bonne route métier », et non que « le système accepte une URL ». Une commande réussie ne garantit pas que la bonne page s’affiche.

Pour une même route, prévoyez au minimum un cas de démarrage à froid et un cas où l’application est au premier plan. Si une authentification intervient, distinguez également les états connecté et non connecté, mais l’état du compte de test doit être établi explicitement par le script et ne jamais être hérité du cas précédent.

Ajouter une sonde observable à la build de débogage

La ligne de commande peut ouvrir un lien, mais elle ne permet pas de déterminer de manière fiable ce que l’application affiche finalement. Dans une build DEBUG, le point d’entrée commun du routage peut écrire le résultat normalisé dans le répertoire de cache. Cette logique ne doit pas être incluse dans la build de production, et aucun jeton ni paramètre de requête complet ne doit être journalisé.

#if DEBUG
func recordResolvedRoute(_ route: String) {
    let payload: NSDictionary = [
        "route": route,
        "recordedAt": Date().timeIntervalSince1970
    ]
    let directory = FileManager.default.urls(
        for: .cachesDirectory,
        in: .userDomainMask
    )[0]
    let file = directory.appendingPathComponent("link-probe.plist")
    payload.write(to: file, atomically: true)
}
#endif

La sonde doit être placée après le routeur commun aux URL Scheme, aux Universal Links et aux redirections issues des notifications. Si l’enregistrement est effectué séparément dans chaque callback du cycle de vie, le test peut seulement démontrer que le callback a été déclenché, sans prouver que les paramètres ont été validés ni que la navigation a atteint la bonne page métier.

Garantir l’atomicité des résultats

Supprimez l’ancien fichier avant chaque exécution et utilisez un remplacement atomique lors de l’écriture. Sinon, un plantage de l’application ou une lecture trop précoce peut conduire le test en cours à réutiliser le résultat précédent. Il est recommandé de ne conserver que la clé de route, le type d’erreur et l’horodatage, après anonymisation des entrées utilisateur.

Stabiliser le simulateur et exécuter la matrice de routage

Dans la CI, n’utilisez pas la cible ambiguë booted comme point de départ. Résolvez d’abord l’UDID à partir du nom de l’appareil et de la version du système, vérifiez que seul le simulateur visé est démarré, puis attendez la fin de l’amorçage. L’exemple suivant suppose que le pipeline a déjà obtenu SIMULATOR_UDID.

set -euo pipefail

xcrun simctl shutdown all
xcrun simctl boot "$SIMULATOR_UDID"
xcrun simctl bootstatus "$SIMULATOR_UDID" -b

xcodebuild \
  -scheme LinkLab \
  -configuration Debug \
  -sdk iphonesimulator \
  -derivedDataPath .ci/DerivedData \
  build

APP_PATH=".ci/DerivedData/Build/Products/Debug-iphonesimulator/LinkLab.app"
xcrun simctl install "$SIMULATOR_UDID" "$APP_PATH"

Après l’installation, récupérez d’abord le conteneur de données. Pour chaque cas, supprimez ensuite la sonde, ouvrez le lien et interrogez périodiquement le résultat. N’utilisez pas un long sleep fixe : lorsque le temps de démarrage de l’application varie, il ralentit inutilement le test ou reste malgré tout insuffisant pour que le fichier soit disponible.

BUNDLE_ID="com.example.linklab"
DATA_DIR="$(xcrun simctl get_app_container \
  "$SIMULATOR_UDID" "$BUNDLE_ID" data)"
PROBE="$DATA_DIR/Library/Caches/link-probe.plist"

rm -f "$PROBE"
xcrun simctl openurl "$SIMULATOR_UDID" "linklab://product/42"

for attempt in 1 2 3 4 5 6 7 8 9 10; do
  test -f "$PROBE" && break
  sleep 1
done

test -f "$PROBE"
ACTUAL="$(/usr/libexec/PlistBuddy -c "Print :route" "$PROBE")"
test "$ACTUAL" = "product/42"

Pour les cas au premier plan, continuez à envoyer les URL après la première ouverture. Pour les cas de démarrage à froid, exécutez d’abord simctl terminate. Pour chaque échec, conservez l’entrée, la valeur attendue, la valeur réelle et les journaux de test, mais n’archivez pas le conteneur complet de l’application s’il contient des informations sensibles.

Les URL Scheme permettent de vérifier le routeur de façon stable, mais ne remplacent pas un contrôle de bout en bout des Universal Links. Ces derniers dépendent également des droits associés aux domaines, du fichier publié sur le site HTTPS, du type de contenu, de l’identifiant de l’application et du cache du système.

Au premier niveau, appelez directement le routeur commun à chaque commit afin de couvrir rapidement la normalisation des chemins, l’encodage en pourcentage, les paramètres dupliqués, les valeurs vides et les routes inconnues. Au second niveau, utilisez de vrais liens HTTPS sur un domaine de test contrôlé, puis réinstallez l’application pour valider le démarrage à froid et le passage au premier plan. Toute modification du fichier d’association du site doit déclencher ce second niveau ; les seuls tests unitaires ne suffisent pas.

Une erreur fréquente consiste à cliquer de nouveau immédiatement après avoir modifié le fichier d’association, puis à interpréter le résultat provenant de l’ancien cache comme celui de la nouvelle configuration. Pour le diagnostic, consignez le numéro de build de l’application, la version du système, l’ordre d’installation et les en-têtes de réponse du domaine de test. Commencez par vérifier que le système transmet bien le lien à l’application, puis examinez le routage métier.

Intégrer les preuves d’échec et le nettoyage au contrôle CI

Un contrôle fiable ne doit pas seulement renvoyer un statut différent de zéro : il doit aussi permettre aux mainteneurs d’identifier rapidement la couche en défaut. Le résumé d’échec doit contenir au minimum le nom du cas, l’état de l’application, le type d’entrée, la route attendue, la route réelle et l’indication de création ou non du fichier de sonde. Si le fichier n’existe pas, vérifiez en priorité l’installation, le point d’entrée du cycle de vie et les déclarations de liens. S’il existe mais contient une mauvaise route, examinez plutôt l’analyseur et l’état de navigation.

La liste de contrôle avant fusion peut rester concise :

  • le modèle du simulateur et la version du système sont fixés par le pipeline ;
  • le démarrage à froid et l’état au premier plan sont testés séparément ;
  • chaque cas commence par nettoyer le processus de l’application ou l’état de la sonde ;
  • des contre-exemples couvrent les caractères spéciaux, les paramètres vides et les chemins inconnus ;
  • les Universal Links font l’objet d’une validation HTTPS distincte ;
  • les artefacts d’échec ne contiennent ni identifiants, ni jetons, ni données utilisateur ;
  • le simulateur est arrêté et le répertoire de build temporaire supprimé à la fin de la tâche.

Enfin, exécutez xcrun simctl shutdown "$SIMULATOR_UDID" et laissez la stratégie de nettoyage de l’espace de travail traiter .ci/DerivedData. Ce n’est qu’en combinant un contrat de routage, une sonde observable et l’isolation des états que les tests de deep links passent d’un simple contrôle « le lien s’ouvre » à une véritable barrière d’ingénierie capable de bloquer les régressions de navigation.

Questions fréquentes

simctl openurl suffit-il pour valider un Universal Link ?

Non. Il valide surtout le comportement de l’application. Il faut aussi tester le domaine HTTPS, le fichier d’association, les droits signés et le cache du système.

Pourquoi tester le démarrage à froid et l’application déjà ouverte ?

Ces scénarios empruntent souvent des callbacks de cycle de vie différents. Les deux sont nécessaires pour détecter les routes perdues et les navigations dupliquées.

ZoomMini Mac dans le cloud

Choisissez un nœud physique dédié pour compiler, tester et expérimenter

Deux configurations M4 sont disponibles sur des nœuds à Singapour, Tokyo, Séoul et Hong Kong. La disponibilité réelle est confirmée par le résultat retourné lors de la commande.

Choisir un modèle et commander