Du premier test Playwright à une suite exploitable en équipe
Ton premier test Playwright passe. Le navigateur s'ouvre, le scénario se déroule, le rapport est vert. Quelques semaines plus tard, vingt tests tournent, trois échouent de temps en temps, et personne ne sait dire si le coupable est l'application, les données ou le test lui-même. C'est le moment où la suite devient un sujet d'équipe, et où connaître la syntaxe ne suffit plus.
Cet article répond à une seule question : qu'est-ce qu'il faut avoir décidé avant d'ajouter le vingt-et-unième scénario ? Il y a quatre décisions. Pour chacune, je te montre ce que Playwright met à disposition pour la tenir, puis je termine sur ce que cela change quand tu choisis comment te former.
Partenariat commercial : dans cet article, je présente la formation Mission Playwright de Matthieu Bridoux, avec qui je suis partenaire.
1. Que protège ce test ?
Un test qui ne nomme pas son risque devient difficile à défendre quand il échoue, et presque impossible à supprimer quand il ne sert plus. Avant d'écrire le code, écris le risque en une phrase : « un client ne peut plus finaliser une commande après un paiement refusé puis accepté ». Si tu n'arrives pas à formuler cette phrase, le scénario n'est pas prêt.
Imagine une boutique en ligne fictive, prise ici comme exemple illustratif. Son équipe a vingt tests : dix passent par le parcours de commande, six vérifient des écrans de compte, quatre des pages éditoriales. Quand on pose la question « quel risque chacun protège-t-il ? », les six tests de compte reprennent en réalité des règles déjà vérifiées plus bas, dans les tests unitaires et d'API. Ils coûtent de la maintenance sans apporter de nouvelle information. La bonne décision n'est pas d'en ajouter dix de plus, c'est d'en retirer ou d'en déplacer quelques-uns.
Voici ce que donne ce risque en test, toujours sur cette boutique fictive (URL et libellés inventés). Le nom du test porte le risque, l'assertion vérifie ce que le client voit, et le prestataire de paiement, qui n'est pas le tien, est remplacé par une réponse que tu maîtrises :
Si un jour ce test échoue, la phrase du titre te dit tout de suite ce qui est menacé, et la personne qui lit le rapport sait si le sujet est urgent.
Playwright t'aide à tenir cette discipline. Sa documentation recommande de tester ce que l'utilisateur voit et fait, pas des détails d'implémentation comme le nom d'une fonction ou une classe CSS. Elle recommande aussi de ne tester que ce que tu contrôles : pour une dépendance tierce, tu interceptes la requête avec page.route et tu garantis la réponse, au lieu de laisser ton test dépendre d'un serveur qui n'est pas le tien (bonnes pratiques Playwright).
Ton arbitrage : le test de bout en bout est en général le plus coûteux à maintenir. Garde-le pour les parcours dont l'échec coûte vraiment à l'utilisateur ou à l'activité, et laisse les règles fines aux niveaux inférieurs.
2. Un échec se comprend-il sans relancer le test ?
Quand un test rouge oblige à le relancer trois fois pour savoir ce qui s'est passé, l'équipe finit par ne plus lui faire confiance. La question utile est donc de savoir, à la lecture d'un échec, s'il vient du produit, des données ou de l'environnement.
Playwright fournit deux outils pour cela. Le premier est la classification des résultats quand tu actives les relances : un test réussi du premier coup est « passed », un test qui échoue puis réussit à la relance est « flaky », un test qui échoue à toutes les relances est « failed ». Par défaut, les tests en échec ne sont pas relancés (documentation des retries). Le piège est de régler les relances pour que le rapport redevienne vert et de ne plus regarder la ligne « flaky ». Un test flaky est un signal, pas un état normal.
Le second outil est le trace viewer. Pour les échecs en CI, la documentation recommande les traces plutôt que les vidéos et les captures d'écran : chronologie, instantanés du DOM à chaque action, requêtes réseau. Elle conseille de les enregistrer à la première relance d'un test en échec, et déconseille de les activer sur tous les tests parce que c'est coûteux en performance (bonnes pratiques).
Une configuration courante donne ceci : aucune relance en local, pour voir les échecs tout de suite, deux relances en CI, et la trace enregistrée à la première relance. Le nombre de relances est un choix d'équipe. La documentation n'en recommande pas : son exemple en utilise trois.
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
retries: process.env.CI ? 2 : 0,
use: { trace: 'on-first-retry' },
});Une cause d'intermittence se corrige dès l'écriture : les assertions. La documentation précise qu'une assertion qui attend, comme await expect(locator).toBeVisible(), relance la vérification jusqu'à ce que la condition soit remplie. Écrite sous la forme expect(await locator.isVisible()).toBe(true), elle ne patiente pas du tout et renvoie le résultat immédiatement. Deux lignes qui se ressemblent, deux comportements différents.
Voici les deux formes côte à côte, pour un message de confirmation qui apparaît une demi-seconde après le clic :
// Ne patiente pas : lit l'état à l'instant et répond tout de suite.
expect(await page.getByText('Commande confirmée').isVisible()).toBe(true);
// Patiente : relance la vérification jusqu'à ce que ce soit vrai, ou jusqu'au timeout.
await expect(page.getByText('Commande confirmée')).toBeVisible();La première version passe sur ta machine quand l'application est rapide, et échoue en CI quand elle l'est moins. C'est exactement le profil d'un test flaky.
Ce que tu décides en équipe : un test flaky est traité avec un responsable et un délai que vous fixez ensemble, et la relance n'est jamais la réponse définitive.
3. Quelqu'un d'autre peut-il modifier ce test ?
Le test que tu as écrit un vendredi soir sera modifié par quelqu'un d'autre dans six mois, peut-être par toi sans que tu t'en souviennes. Trois choix rendent cette reprise possible.
Le premier concerne les locators. La documentation recommande de privilégier ce que l'utilisateur perçoit, comme le rôle et le libellé d'un bouton, plutôt qu'un sélecteur CSS accroché à la structure de la page. Un bouton identifié par getByRole('button', { name: 'submit' }) survit à un changement de classes décidé par un designer, ce qui n'est pas le cas d'une longue chaîne de classes.
Sur le bouton de paiement de notre boutique fictive, la différence ressemble à ceci :
// Fragile : dépend des classes choisies par le front.
page.locator('button.btn.btn-primary.checkout-submit');
// Résistant : s'appuie sur ce que l'utilisateur perçoit.
page.getByRole('button', { name: 'Payer' });Si le designer renomme les classes demain, le premier locator casse et le second continue de fonctionner.
Le deuxième est l'isolation. Chaque test doit pouvoir tourner seul, avec son propre état : stockage local, cookies, données. C'est ce qui permet de relancer un test seul et d'éviter qu'un échec en entraîne dix autres. La documentation ajoute qu'un peu de duplication reste acceptable quand les tests sont simples, si cela les rend plus lisibles.
Quand le rôle et le libellé ne suffisent pas, data-testid avec getByTestId() reste le plus résistant aux changements, au prix d’un attribut à maintenir dans le code de l’application.
Le troisième est la structure du setup. Les fixtures de Playwright rassemblent au même endroit la préparation et le nettoyage, se réutilisent d'un fichier à l'autre, ne s'exécutent que si le test en a besoin et peuvent dépendre les unes des autres (documentation des fixtures). Le Page Object Model garde sa place : la documentation montre elle-même des fixtures qui fournissent des objets de page. La question n'est donc pas « POM ou fixtures », mais à quel moment une abstraction coûte moins cher que la duplication qu'elle remplace. Une couche très lourde construite par habitude, avant d'en avoir besoin, est plus difficile à reprendre que trois tests un peu répétitifs.
Voici l'idée appliquée à un panier fictif, adaptée de l'exemple de la documentation : l'ajout d'un produit et le nettoyage vivent au même endroit, et chaque test qui en a besoin le reçoit en argument.
// cart.spec.ts
import { test, expect } from './fixtures';
test('retirer le dernier article vide le panier', async ({ cartPage }) => {
await cartPage.removeProduct('Casque audio');
await expect(cartPage.emptyMessage).toBeVisible();
});Ton arbitrage : structure quand la duplication te coûte plus que l'indirection, pas avant.
4. Qui traite les alertes et décide des corrections ?
Une suite qui tourne dans la CI et dont personne ne lit le rapport protège moins qu'une petite suite lue chaque matin. Cette décision est la moins technique des quatre, et c'est souvent celle qui manque.
Côté outil, la documentation conseille de lancer les tests fréquemment, idéalement à chaque commit et à chaque pull request, de privilégier Linux en CI parce que c'est moins cher, et d'envisager le parallélisme pour accélérer l'exécution quand la suite grossit (bonnes pratiques). Ces réglages relèvent de la configuration. Ils ne répondent pas à la question de savoir qui agit quand le rapport est rouge.
Trois points à fixer avec l'équipe, par écrit :
- Qui lit le rapport après chaque exécution, et dans quel délai ?
- Qui peut décider de mettre un test de côté, par exemple avec une annotation comme
test.fixme, et avec quelle trace dans le backlog pour qu'il ne disparaisse pas ? - Qui arbitre quand un test révèle un défaut produit, mais que la livraison est prévue le jour même ?
La troisième question n'est pas technique. C'est une décision de risque, qui se prend avec le product owner et les développeurs, pas toute seule. Une suite d'automatisation sans réponse à ces trois questions finit par devenir le projet d'une seule personne.
Un cas illustratif pour voir cette décision en situation. Jeudi, 17 heures : le test de paiement échoue sur la branche prévue en livraison le lendemain matin. La trace montre que le bouton « Payer » reste désactivé quand le champ du code promo est vide. Il y a trois façons de réagir, et chacune a un décideur différent :
Option | Ce que l'équipe accepte | Qui tranche |
Corriger avant la livraison | Un décalage possible de quelques heures | Le product owner, avec le développeur concerné |
Livrer avec le défaut connu et documenté | Un risque sur le paiement pendant la période | Le product owner, avec la direction produit |
Reporter la livraison | Un retard sur la date annoncée | Le product owner, avec le sponsor de la date |
Ton rôle de QA est d'apporter les faits : quel parcours est touché, dans quels cas, avec quel contournement. Le choix appartient à ceux qui portent la décision. Ce qui compte, c'est que la question ait été posée avant le jeudi 17 heures.
Se former : ce que propose Mission Playwright, et pour qui
Tout ce qui précède se trouve dans la documentation de Playwright, qui est bien faite. Une formation ne remplace pas cette lecture. Elle sert à raccourcir le chemin : quelqu'un relit ton code, te dit ce qui coûtera cher dans six mois et t'évite de construire dans la mauvaise direction pendant des semaines. À toi de juger si cela vaut l'investissement dans ta situation.
Matthieu Bridoux a conçu Mission Playwright pour cela.
La formation part des bases et vise d'abord les testeurs manuels qui passent à l'automatisation. Si tu pratiques déjà Playwright en équipe depuis un moment, demande à Matthieu ce que le programme t'apporterait avant de t'engager. Et quel que soit ton niveau, compare le programme à ton objectif du moment : passer du manuel à l'automatisation n'appelle pas le même accompagnement que consolider une suite existante.
À retenir
- Écris le risque protégé en une phrase avant d'écrire le scénario, et retire les tests qui répètent un risque déjà couvert plus bas.
- Traite un test flaky comme une alerte, avec un responsable et un délai, pas comme un état normal que les relances masquent.
- Choisis tes locators sur ce que l'utilisateur voit et garde chaque test isolé, avant de penser à la structure.
- Mets en place fixtures ou Page Object Model quand la duplication te coûte plus que l'indirection.
- Décide par écrit qui lit le rapport, qui met un test de côté et qui arbitre un défaut avant une livraison.
FAQ
Faut-il savoir coder pour apprendre Playwright ?
Pour démarrer, pas forcément. La documentation précise que TypeScript fonctionne sans configuration particulière et qu'aucune expérience préalable n'est nécessaire. Mission Playwright indique de son côté partir des bases pour les testeurs manuels. Dans une suite partagée, en revanche, tu liras et modifieras du code tous les jours : autant t'y habituer tôt.
Combien de relances faut-il configurer ?
Il n'y a pas de valeur universelle. Par défaut, Playwright ne relance rien, et la documentation utilise trois relances dans son exemple. Garde un nombre faible, et regarde chaque semaine combien de tests sont classés « flaky » : si ce nombre monte, les relances cachent un problème.
Faut-il un Page Object Model avec Playwright ?
Ce n'est pas obligatoire. La documentation propose le POM et les fixtures, et montre même les deux ensemble. Commence simple, et introduis une structure quand la duplication devient plus coûteuse que l'indirection.
Un test flaky doit-il être supprimé ?
Pas automatiquement. Regarde d'abord la trace pour savoir si l'instabilité vient du test, des données ou de l'environnement. Si le risque protégé n'a plus d'intérêt, ou si le test coûte plus qu'il ne rapporte, supprime-le. Sinon, mets-le de côté avec une annotation et un ticket, pour qu'il ne tombe pas dans l'oubli.
Mission Playwright convient-elle si je pratique déjà Playwright ?
La page de la formation vise d'abord les testeurs manuels, avec un parcours de 12 semaines qui part des bases. Si tu as déjà de l'expérience, demande à Matthieu ce que le programme t'apporterait avant de t'engager. Le tarif est sur devis, un paiement échelonné est possible, et la formation n'est pas finançable par le CPF.
Aller plus loin avec Mission Playwright
Si tu es testeur manuel et que tu veux passer à l'automatisation avec Playwright, regarde le programme de Mission Playwright et vérifie qu'il correspond à ton niveau et à ton objectif : Découvrir Mission Playwright
Mention : partenariat commercial avec Matthieu Bridoux.