Les tests instables coûtent aux équipes d'ingénierie 15 à 20 heures par semaine en relances, investigations et releases retardées. Ce coût s'aggrave à mesure que la confiance dans la suite de tests s'érode.

Ce que coûtent vraiment les tests instables

Un test instable est un test qui produit des résultats différents sur le même code : il passe parfois, échoue d'autres fois, sans raison déterministe.

Le coût visible : un ingénieur voit un test échouer, le relance, il passe, il le marque "instable" et continue. Cinq minutes perdues.

Le coût invisible est plus important.

La perte de confiance dans la suite. Une fois que les ingénieurs savent que certains tests sont instables, ils commencent à traiter les échecs comme "probablement instables" par défaut. Les vrais échecs sont écartés. Les bugs passent en production. La suite de tests est devenue du bruit. La surcharge des relances. Une suite avec 10% de tests instables configurée pour 3 tentatives exécute effectivement 130% de ses tests à chaque run CI. Sur une suite de 30 minutes, ça fait 9 minutes par run, des heures par jour pour une équipe. Le temps développeur en investigation. Chaque nouvel échec demande à quelqu'un de décider : est-ce instable ou est-ce réel ? Cette décision prend 5 à 30 minutes d'investigation. À 10 échecs instables par jour, c'est une heure de temps ingénieur par jour. Les releases retardées. Si votre CI est configuré pour bloquer le déploiement en cas d'échec de test (comme il se doit), les tests instables deviennent des bloquants de release. La solution de contournement (désactiver les tests) supprime la couverture sur laquelle vous comptiez. Estimation chiffrée : avec 5% de taux d'instabilité sur une suite de 1 000 tests et 20 runs CI par jour, une équipe perd probablement 15 à 20 heures-ingénieur par semaine en surcharge liée à l'instabilité. À 60 $/heure en coût complet, c'est 900 à 1 200 $/semaine, soit 50 000 à 60 000 $/an.

Ce n'est pas théorique. Google a publié une étude en 2016 montrant qu'1 test sur 7 dans leur monodépôt présentait de l'instabilité à un moment ou un autre. Les chiffres dans la plupart des entreprises sont pires, pas meilleurs.

Les causes les plus courantes d'instabilité

Avant de comparer les frameworks, il faut comprendre ce qui cause l'instabilité.

Conditions de course avec l'UI asynchrone. Le test clique sur un bouton, puis vérifie immédiatement un résultat qui n'a pas encore chargé. Le timing fonctionne sur une machine rapide, échoue sur un serveur CI lent. État de test partagé. Le test A crée des données, le test B les lit, le test C les supprime. S'ils s'exécutent dans un ordre différent ou en parallèle, ils s'interfèrent mutuellement. Timing environnemental. Le test passe en local (SSD rapide, réseau rapide) mais échoue en CI (machine plus lente, latence réseau vers l'environnement de test). Délais d'animation. Une animation CSS se termine pendant que le test essaie d'interagir avec l'élément animé. Timing réseau. Un appel API prend 500 ms en moyenne mais occasionnellement 2 000 ms. Le timeout du test est à 1 000 ms. Dépendances externes. Les tests qui communiquent avec de vrais services tiers (processeurs de paiement, fournisseurs d'email) sont instables à chaque fois que ces services ont des problèmes.

Comment l'architecture de Playwright réduit l'instabilité

Playwright a été conçu dès le départ avec le problème d'instabilité en tête. Deux mécanismes clés.

L'auto-attente

Chaque action et assertion Playwright attend automatiquement que l'élément cible soit dans le bon état avant de continuer.

page.click() attend que l'élément :
  • Existe dans le DOM
  • Soit visible
  • Ne soit pas recouvert par un autre élément
  • Ne soit pas en train d'animer
  • Soit activé
expect(locator).toBeVisible() attend jusqu'au timeout configuré que l'assertion devienne vraie.

Cela élimine la source d'instabilité la plus courante : la condition de course "l'élément existe mais n'est pas encore prêt". Vous n'ajoutez pas de waitForSelector partout. Vous écrivez juste click et Playwright gère l'attente.

Les assertions web-first

La bibliothèque d'assertions de Playwright (expect) est construite pour les UI asynchrones. Quand vous écrivez :

await expect(page.getByText('Succès')).toBeVisible();

Ce n'est pas un snapshot de l'état à un instant précis. Cela interroge le DOM de façon répétée jusqu'à ce que l'assertion devienne vraie, ou jusqu'à l'expiration du timeout. Le timeout par défaut est de 5 secondes.

Comparez à une assertion naïve :

// Instable — vérifie l'état à cet instant précis, pas quand il devient vrai
const text = await page.textContent('.message');
expect(text).toBe('Succès');

La version Playwright réessaie ; la version naïve non. La plupart de l'instabilité dans les suites basées sur Selenium vient de ce pattern naïf, parce que Selenium n'a pas d'assertions avec retry intégré.

Playwright vs Selenium : comparaison d'instabilité

C'est là que la différence architecturale se traduit en chiffres réels.

La comparaison publique la plus complète est le rapport Checkly State of Testing 2024, qui a interrogé environ 1 500 équipes d'ingénierie :

  • Les équipes utilisant Playwright ont signalé 12% de leurs tests comme régulièrement instables
  • Les équipes utilisant Selenium ont signalé 28% régulièrement instables
  • Les équipes utilisant Cypress ont signalé 18% régulièrement instables

La méthodologie compte : "régulièrement instable" signifie que l'équipe a identifié le test comme instable et utilise une solution de contournement. Beaucoup plus de tests sont intermittents mais n'ont pas encore été signalés.

La différence se résume à quelques points précis.

Selenium exige des attentes explicites. driver.findElement(By.id("result")) échoue immédiatement si l'élément n'est pas présent. Les équipes Selenium expérimentées ajoutent des WebDriverWait partout. Les moins expérimentées ne le font pas de façon cohérente, et les tests sont instables. Les attentes par défaut de Playwright conviennent aux SPA modernes. Les applications React et Vue s'affichent de façon asynchrone. L'auto-attente de Playwright a été conçue pour ça. Selenium a été conçu pour les pages rendues côté serveur traditionnelles et adapté aux SPA ; l'adaptation se voit. L'interception réseau de Playwright est intégrée. Mocker les réponses API pour rendre les tests déterministes est une fonctionnalité de première classe dans Playwright. Dans Selenium, ça nécessite des outils supplémentaires (BrowserMob Proxy, etc.) qui ajoutent de la complexité et leurs propres modes d'échec.

Le vrai coût de l'instabilité Selenium en pratique

Une équipe migrant de Selenium vers Playwright dans une entreprise SaaS de taille moyenne a documenté son expérience.

Avant la migration :

  • 1 200 tests Selenium
  • ~180 tests marqués "instables connus" (15%)
  • Temps de run CI : 45 minutes avec 3 tentatives
  • ~8 heures-ingénieur par semaine en investigation d'instabilité
  • Tests désactivés à cause de l'instabilité : 47 (4%)

Après la migration vers Playwright (6 mois plus tard) :

  • 1 200 tests migrés, ~200 nouveaux tests ajoutés
  • ~40 tests marqués instables (2,5%)
  • Temps de run CI : 22 minutes avec 1 tentative
  • ~1 heure-ingénieur par semaine sur l'instabilité
  • Tests désactivés : 5 (0,3%)

La réduction du temps d'exécution vient en partie de Playwright étant plus rapide (exécution parallèle, pas d'overhead WebDriver), mais surtout de la nécessité de moins de tentatives.

Comment mesurer votre coût d'instabilité actuel

Avant de corriger l'instabilité, mesurez-la. La plupart des systèmes CI la suivent si vous savez où chercher.

GitHub Actions : vérifiez les artifacts de rapport de test. La plupart des reporters Playwright incluent un statut "flaky" pour les tests qui passent en retry. Reporter HTML Playwright : dans playwright.config.ts, ajoutez

reporter: [['html', { open: 'never' }]],

Le rapport HTML marque les tests qui ont passé en retry. Ce sont vos tests instables.

Suivi manuel : exécutez votre suite de tests 10 fois sur le même commit. Tout test qui ne produit pas le même résultat à chaque fois est instable. Fastidieux mais définitif. Calculez le coût :

1. Comptez le nombre de tests instables uniques

2. Estimez le temps d'investigation moyen par échec (généralement 10 à 20 minutes)

3. Estimez combien de fois chaque test instable échoue par semaine

4. Multipliez : (échecs de tests instables par semaine) × (minutes par investigation) / 60 = heures-ingénieur par semaine

Pour la plupart des équipes, ce chiffre est surprenant.

Corriger les tests instables dans Playwright

Quand vous avez des tests instables dans une suite Playwright, l'approche de diagnostic :

Étape 1 : exécuter avec la trace activée

// playwright.config.ts
use: {
  trace: 'on-first-retry',
}

Au retry, Playwright enregistre une trace complète : captures d'écran, requêtes réseau, snapshots DOM, sortie console. Ouvrez avec npx playwright show-trace trace.zip.

Étape 2 : identifier le point d'échec

La trace montre exactement où le test a échoué et quel était l'état de la page. La cause est généralement l'une de ces trois : l'élément n'était pas encore visible, la requête réseau n'était pas terminée, ou le test précédent a laissé un état qui a affecté celui-ci.

Étape 3 : corriger avec des assertions web-first

Remplacez les patterns fragiles :

// Fragile
await page.waitForTimeout(2000);
await page.click('.submit-button');

// Robuste
await page.getByRole('button', { name: 'Envoyer' }).click();
// L'auto-attente gère le timing

Étape 4 : corriger l'état partagé

Chaque test doit configurer ses propres données et ne pas dépendre d'autres tests. Utilisez des hooks beforeEach et des appels API pour créer l'état dont chaque test a besoin.

test.beforeEach(async ({ request }) => {
  // Créer un utilisateur frais pour chaque test via l'API
  await request.post('/api/users', { data: { email: 'test@example.com' } });
});

FAQ

Nous avons 500 tests Selenium. La migration vers Playwright vaut-elle le coup ?

Pour la plupart des équipes : oui, si l'instabilité est un vrai problème. La durée de migration est d'environ 2 à 4 semaines de temps ingénieur pour 500 tests, selon la complexité. Les économies continues (moins de temps CI, moins de temps d'investigation, suite plus fiable) le justifient généralement en quelques mois.

Nos tests Playwright sont toujours instables. Que faisons-nous de travers ?

La plupart de l'instabilité Playwright vient d'un état de test partagé (tests qui dépendent les uns des autres) ou du contournement de l'auto-attente avec des waitForTimeout. Vérifiez la présence de setTimeout et waitForTimeout dans vos fichiers de test. Remplacez chaque occurrence par une assertion correcte.

Quel est un objectif d'instabilité raisonnable ?

En dessous de 2% est atteignable. En dessous de 1% pour une suite bien maintenue est réaliste. Zéro est l'objectif ; 0,5% est bien. Au-dessus de 5%, l'instabilité affecte la productivité de l'équipe et la suite perd sa crédibilité.

L'auto-attente de Playwright élimine-t-elle toutes les conditions de course ?

Non. Elle élimine les plus courantes (élément pas prêt pour l'interaction, assertion échouant parce que le résultat n'est pas encore apparu). Les conditions de course dans la logique applicative dépassent ce que le framework peut résoudre. Les tests qui dépendent les uns des autres ou partagent un état de base de données nécessitent une isolation explicite.

→ See also: Déboguer les Tests Instables: Un Guide Pratique | Playwright vs Cypress vs Selenium en 2026: Comparaison Honnête