Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🧪 QA Flaky Tests Patterns

Démonstration de patterns anti-flaky : anti-patterns courants qui rendent une suite de tests instable, et leur correction.

Cible de test : the-internet.herokuapp.com — site de démonstration public conçu spécifiquement pour illustrer des comportements asynchrones (chargements dynamiques, éléments activés après délai).


🎯 Objectif

Une suite de tests "flaky" (instable) échoue de façon aléatoire, sans lien avec un vrai bug applicatif — souvent à cause d'hypothèses de timing fragiles. Ce repo illustre, sur des cas concrets et reproductibles, comment identifier et corriger ces anti-patterns.

Chaque scénario est démontré en deux versions :

  • before/ : la version instable (anti-pattern), volontairement fragile
  • after/ : la version fiabilisée, qui exploite les mécanismes natifs de Cypress plutôt que des attentes arbitraires

🧱 Stack technique

Cypress · TypeScript


📂 Structure du projet

qa-flaky-tests-patterns/
├── cypress/
│   ├── e2e/
│   │   ├── before/
│   │   │   ├── dynamic-loading.cy.ts          # cy.wait() fixe → flaky selon la charge
│   │   │   ├── dynamic-controls.cy.ts         # interaction avant fin d'activation → race condition
│   │   │   └── retry-on-transient-error.cy.ts # aucun retry → flaky sur erreur transitoire
│   │   └── after/
│   │       ├── dynamic-loading.cy.ts          # assertion avec retry natif Cypress
│   │       ├── dynamic-controls.cy.ts         # attente explicite de l'état avant interaction
│   │       └── retry-on-transient-error.cy.ts # retry automatique sur erreur 5xx
│   └── support/
│       └── retry.ts   # fonction de retry réutilisable, testable en isolation
├── .github/workflows/
│   └── flaky-tests-demo.yml
├── cypress.config.ts
└── package.json

🔴 Pattern 1 — Attente fixe (cy.wait(ms)) vs assertion avec retry

Avant (anti-pattern) :

cy.contains("Start").click();
cy.wait(3000); // pari sur une durée fixe : flaky si le chargement réel varie
cy.get("#finish").should("be.visible");

Après (fiabilisé) :

cy.contains("Start").click();
// Cypress retente automatiquement l'assertion jusqu'à son timeout configuré
cy.get("#finish", { timeout: 10000 }).should("be.visible");

Le cy.wait(ms) fixe part du principe qu'un chargement dure toujours le même temps. En réalité, ce temps varie selon la charge serveur, le réseau, ou l'environnement d'exécution (CI vs local). Une assertion Cypress standard (should) retente automatiquement jusqu'à expiration de son timeout — elle s'adapte à la durée réelle au lieu de parier dessus.


🔴 Pattern 2 — Interaction avant fin d'activation (race condition)

Avant (anti-pattern) :

cy.get("#input-example button").click();
cy.get("#input-example input").type("test"); // le champ peut être encore désactivé

Après (fiabilisé) :

cy.get("#input-example button").click();
cy.get("#input-example input").should("not.be.disabled").type("test");

Cliquer sur le bouton déclenche une activation asynchrone du champ. Sans attendre explicitement la fin de cette transition, Cypress peut tenter de saisir du texte dans un champ encore désactivé — un comportement qui passe ou échoue selon la rapidité d'exécution de la machine.


🔴 Pattern 3 — Absence de retry sur erreur transitoire (502)

Avant (anti-pattern) :

const response = await flakyCall(); // une seule tentative
expect(response.status).to.eq(200); // échoue si le service répond un 502 ponctuel

Après (fiabilisé) :

const response = await requestWithRetry(flakyCall, 3); // retente jusqu'à 3 fois
expect(response.status).to.eq(200); // absorbe les erreurs transitoires

Un service peut répondre ponctuellement une erreur 5xx (surcharge momentanée, redémarrage, latence réseau) sans que cela reflète une vraie régression. requestWithRetry (voir cypress/support/retry.ts) rejoue automatiquement l'appel jusqu'à obtenir une réponse valide ou épuisement des tentatives — ce qui absorbe ces erreurs transitoires sans masquer une vraie panne persistante (si toutes les tentatives échouent, l'échec est bien remonté).


⚙️ Pipeline CI

Le pipeline exécute uniquement les versions fiabilisées (after/) comme critère de réussite. Les versions instables (before/) sont aussi exécutées à titre démonstratif, dans un job séparé configuré en continue-on-error: true — leur échec est attendu et n'affecte pas le statut global du pipeline.


🚀 Exécution

npm install

# Reproduire les échecs (versions instables)
npx cypress run --spec "cypress/e2e/before/**/*.cy.ts"

# Vérifier les versions fiabilisées
npx cypress run --spec "cypress/e2e/after/**/*.cy.ts"

📸 Preuve d'exécution réelle

flakyTestSummary flakyResults

👤 Auteur

Mohamed Touaoua — QA Automaticien Ce projet fait partie de mon portfolio d'automatisation QA.

About

Anti-patterns de tests flaky (Cypress) et leur correction — attente fixe, race condition, retry sur erreur transitoire

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages