Chapitre 14.4 — E2E avec Maestro
Où on en est : logique et écrans testés isolément. Le dernier étage : des PARCOURS complets — l’app réelle, installée sur l’émulateur, pilotée par un robot qui tape, scrolle et vérifie. L’outil 2026 : Maestro, des flows en YAML lisibles par un humain.
⏱️ TL;DR — Maestro pilote l’APK réel sur l’émulateur : des fichiers YAML déclaratifs (
tapOn,assertVisible,inputText), tolérants aux lenteurs (attentes automatiques). On écrit LES 3 flows qui comptent : le smoke test (l’app démarre), la partie complète (jouer → résultat → historique), et le parcours réglages. À lancer avant chaque release native — 3 minutes de robot qui remplacent 15 minutes de doigts.
🎯 Objectifs
- Installer Maestro et lancer un premier flow sur l’émulateur.
- Écrire les 3 flows de Drill (smoke, partie complète, réglages).
- Fiabiliser : sélecteurs stables, données de test, le problème du hasard.
- Situer l’E2E dans la pyramide (quoi ici, quoi en dessous).
Installation & premier flow
# Windows : via WSL ou le binaire — la doc maestro.dev guide ; vérification :
maestro --versionUn flow = un YAML dans .maestro/ :
# .maestro/smoke.yaml — l'app démarre et affiche l'accueil
appId: com.alex.drill
---
- launchApp
- assertVisible: "Drill"
- assertVisible: "Quiz rapide"# Émulateur lancé, APK installé (build preview — ch. 12.2) :
maestro test .maestro/smoke.yamlMaestro parle à l’app par l’arbre d’accessibilité (les textes visibles, nos accessibilityLabel) — pas de compilation spéciale, pas d’instrumentation : l’APK de release EXACT que vous distribuez. C’est sa force face à l’ancêtre Detox (qui exigeait un build dédié) : zéro friction, et l’app testée = l’app livrée.
Le flow qui compte : la partie complète
# .maestro/partie-complete.yaml
appId: com.alex.drill
---
- launchApp:
clearState: true # pm clear : départ vierge reproductible
- tapOn: "Thèmes"
- tapOn: "Symfony 7"
- tapOn: "Lancer une session"
# ── la boucle de jeu : 5 questions ──
- repeat:
times: 5
commands:
- tapOn:
id: "answer-0" # ← testID : le hasard rend le TEXTE imprévisible
- tapOn: "Question suivante"
- assertVisible: ".*pts.*" # l'écran résultat (regex)
- tapOn: "Retour à l'accueil"
- tapOn: "Historique"
- assertVisible: "Symfony" # la session est enregistréeLes leçons de terrain condensées dans ce flow :
clearState: true: chaque run part d’une app vierge — la reproductibilité avant tout (le pm clear du ch. 9.1, automatisé).- Le hasard oblige les testID : les questions étant tirées au sort,
tapOn: "#[Route]"ne marche qu’une fois sur N. D’oùtestID="answer-0"posé sur le premier AnswerButton (le testID du ch. 3.2 trouve son heure). On tape « le premier choix », pas « la bonne réponse » — le flow teste le PARCOURS, pas le score. - Les assertions en regex (
.*pts.*) absorbent les valeurs variables.
Troisième flow (réglages) : basculer un switch, changer la durée du timer, vérifier la
persistance après stopApp/launchApp (SANS clearState cette fois — c’est le test de
l’AsyncStorage réel).
Fiabiliser : la doctrine anti-flaky
L’E2E a une réputation de fragilité — les règles qui la démentent :
- Sélecteurs stables : du texte UTILISATEUR quand il est déterministe, des testID quand il ne l’est pas. Jamais de coordonnées.
- Peu de flows, souvent lancés : 3 flows fiables battent 30 flows douteux. Le critère d’entrée : « si CE parcours casse, la release est annulée ».
- Données maîtrisées : clearState + (idéalement) un repo de questions de TEST pointé par la variante dev (ch. 11.3 — extra.questionsBaseUrl : la boucle est bouclée).
- Les attentes sont intégrées (Maestro attend qu’un élément apparaisse) — si un flow a
besoin de
waitForAnimationToEndpartout, c’est l’app qui est lente, pas le test qui est impatient.
La pyramide finale : la base court à chaque commit (2 s), le milieu à chaque PR/feature, le sommet avant chaque release native. Chaque étage attrape ce que l’étage du dessous ne PEUT pas voir : Jest ignore le rendu, RNTL ignore le natif réel, Maestro voit tout mais lentement.
⚠️ Piège — L’E2E sur l’APK release teste AUSSI vos intégrations réelles : le flow partie-complète FETCH le vrai repo GitHub. Deux positions défendables : l’assumer (le test couvre le réseau — mais il casse si GitHub tousse) ou l’isoler (repo de test + variante). Le cours tranche : smoke sur la prod réelle (ça teste TOUT le pipeline), flows de jeu sur le repo de test (déterminisme). Documentez le choix — le collègue de dans 6 mois (vous) doit savoir pourquoi le flow exige la variante dev.
💡 Pour un dev Next.js — Maestro est votre Playwright : YAML au lieu de TS, l’arbre d’accessibilité au lieu du DOM, l’émulateur au lieu du navigateur headless. Même philosophie de sélecteurs (user-facing d’abord), même doctrine anti-flaky, même place dans la pyramide. Le YAML surprend puis convainc : un flow se RELIT comme un scénario de test manuel — même par un non-dev.
✏️ Exercices
1. Écrivez et faites passer les 3 flows sur votre émulateur. Chronométrez la suite complète et comparez au même parcours à la main.
✅ Solution
Attendu : smoke ~20 s, partie ~60-90 s, réglages ~40 s — total < 3 min contre ~10-15 min à la main (et le robot ne se lasse jamais, lui). Le moment de conversion habituel : la troisième fois qu’on lance la suite au lieu de re-tester manuellement — le réflexe est né.
2. Le flow réglages : écrivez le test de persistance complet (changer timer à 30 s, tuer, relancer, vérifier) — et expliquez pourquoi clearState y serait un contresens.
✅ Solution
- launchApp: { clearState: true } # départ propre UNE fois
- tapOn: "Réglages"
- tapOn: "30"
- stopApp
- launchApp # SANS clearState : c'est LE test
- tapOn: "Réglages"
- assertVisible:
id: "timer-30-selected" # ou l'état visuel du badge sélectionnéclearState au RE-lancement effacerait précisément ce qu’on teste (la persistance AsyncStorage). Le clearState initial + son absence ensuite ENCODE le scénario : « app fraîche → réglage → survie au redémarrage ».
3. Intégrez la suite au rituel : ajoutez une section « E2E » à la checklist de release native (ch. 12.6), avec la commande unique et le critère de blocage.
✅ Solution
Checklist, après le build : « [ ] maestro test .maestro/ sur l’APK candidat (émulateur
Pixel8-API35) — 3/3 verts sinon RELEASE ANNULÉE ». La commande sans argument lance tout le
dossier. Le mot « annulée » écrit noir sur blanc est ce qui distingue un garde-fou d’une
suggestion — un E2E rouge qu’on « regardera plus tard » ne protège rien.
🧠 Quiz
1. Sur quoi Maestro s’appuie-t-il pour trouver les éléments ?
Réponse
L’arbre d’accessibilité de l’app réelle (textes visibles, labels, testID) — pas d’instrumentation ni de build spécial : l’APK testé est l’APK livré.
2. Pourquoi les flows de jeu utilisent-ils des testID plutôt que le texte ?
Réponse
Le hasard du tirage rend les textes imprévisibles : answer-0 (le premier choix) est
stable, « #[Route] » ne l’est pas. On teste le parcours, pas le contenu tiré.
3. Que fait clearState et quand l’omettre volontairement ?
Réponse
Il vide les données de l’app au lancement (reproductibilité). On l’omet quand la persistance EST le sujet du test (réglages qui survivent au redémarrage).
4. Récitez la pyramide de Drill et la cadence de chaque étage.
Réponse
~30 Jest lib/stores (chaque commit, 2 s) → 5-6 écrans RNTL (chaque feature) → 3 flows Maestro (chaque release native, ~3 min). Chaque étage voit ce que le dessous ne peut pas.
5. Quel est le critère d’entrée d’un parcours en E2E ?
Réponse
« Si ce parcours casse, la release est annulée » — les flows E2E sont peu nombreux, critiques et bloquants ; tout le reste descend dans la pyramide.
👉 Chapitre suivant : 14.5 — Sentry & la perf — voir les crashs de production, et le mot de la fin sur la performance.