Introduction & Contexte
La Qualité Logicielle a longtemps été perçue comme une activité périphérique, presque secondaire par rapport au développement. Historiquement, le test était vu comme une étape post-développement, une phase coûteuse et souvent sacrifiée lorsque les délais de mise en production devenaient trop serrés. Cette vision réductrice a contribué à faire de la QA un domaine sous-estimé, parfois limité à des vérifications manuelles répétitives ou à des campagnes de tests peu structurées.
Pourtant, l’évolution des systèmes a changé la donne : l’avènement des microservices, la généralisation du mobile, la complexité du retail et la montée en puissance du cloud ont fait de la QA une discipline clé, au même titre que le développement ou l’architecture logicielle. Dans un environnement distribué et en constante évolution, la qualité n’est plus un luxe, mais une condition de survie.
« Un bug critique en production ne se mesure pas seulement en coûts techniques, mais aussi en perte de confiance des utilisateurs et en impact sur l’image de l’entreprise. »
Aujourd’hui, force est de constater que la majorité des frameworks de tests souffrent de maux récurrents :
- Spaghetti code : des tests écrits dans l’urgence, sans structure claire.
- Duplication : les mêmes étapes recopiées dans des dizaines de scripts.
- Dépendances implicites : un test dépend d’un autre sans que ce soit explicité.
- Tests instables (flaky) : un jour verts, un jour rouges, sans changement de code.
C’est précisément dans ce contexte qu’est né le concept de Clean QA : une approche pragmatique et durable, inspirée de Clean Code et du Software Craftsmanship, mais appliquée spécifiquement au domaine du test automatisé. L’ambition est simple :
- Pérennité : les tests doivent survivre aux refactorings et aux changements d’architecture.
- Lisibilité : un test doit pouvoir être compris en quelques secondes, même par un non-technicien.
- Valeur ajoutée : chaque test doit démontrer son utilité métier, et non pas seulement cocher une case de couverture.
Exemple concret
Prenons une équipe e-commerce qui gère 2000 tests automatisés Selenium. Faute d’architecture, chaque test contient ses propres identifiants CSS, ses propres données en dur, et ses propres fonctions utilitaires. Résultat : lorsqu’une classe CSS change dans l’application, 300 tests cassent en même temps.
En adoptant les principes de Clean QA, on centralise ces sélecteurs, on factorise les données, et on applique des patterns comme Page Object ou Screenplay. Résultat : un changement technique n’impacte que 2 ou 3 fichiers centraux, et non des centaines de scripts.
Clean QA se positionne donc comme une discipline à part entière : ni purement technique, ni purement méthodologique. C’est une philosophie appliquée, qui cherche à redonner aux tests leur rôle premier : sécuriser, documenter et accompagner l’évolution des systèmes avec efficacité.
Objectifs pédagogiques
- Concevoir des frameworks de tests robustes et évolutifs.
- Appliquer les patterns de conception au test automatisé.
- Instaurer un socle de bonnes pratiques QA partagées.
- Comprendre les anti-patterns et savoir les éviter.
- Développer une culture QA centrée sur la valeur métier.
Philosophie Clean QA
La philosophie Clean QA est avant tout un état d’esprit. Elle repose sur l’idée que les tests automatisés ne sont pas de simples scripts jetables, mais des actifs stratégiques de l’entreprise. À l’image du mouvement Clean Code, Clean QA propose un ensemble de valeurs et de principes permettant de guider les équipes vers des choix plus réfléchis, plus durables, et orientés vers la lisibilité, la simplicité et l’adaptabilité.
Là où certaines approches de tests se focalisent uniquement sur la couverture ou la vitesse d’exécution, Clean QA met en avant la notion de valeur métier. Autrement dit, un test bien conçu n’est pas seulement celui qui passe rapidement, mais celui qui démontre clairement sa pertinence pour le produit et pour l’équipe.
Le manifeste Clean QA
Le manifeste s’inspire directement du manifeste Agile et du mouvement Craftsmanship. Il propose une série de valeurs contrastées, sous la forme « nous privilégions ceci plutôt que cela ». Ces contrastes permettent de rappeler que chaque décision technique a un impact sur la pérennité des tests.
Exemple de valeurs Clean QA
- Lisibilité du test > Astuce technique.
- Valeur métier > Couverture brute.
- Framework modulaire > Monolithe complexe.
- Prévention de la dette QA > Refactoring tardif.
Ces valeurs ne signifient pas que la couverture ou la performance n’ont aucune importance, mais qu’elles ne doivent jamais se faire au détriment de la lisibilité et de la valeur métier. Un test obscur mais rapide est une bombe à retardement : il deviendra un fardeau pour l’équipe dès qu’il faudra l’analyser ou le corriger.
La carte des valeurs
La carte des valeurs Clean QA peut être représentée comme une boussole. Elle aide les équipes à arbitrer leurs choix quotidiens. Lorsqu’un dilemme se présente — par exemple, entre écrire un test ultra-optimisé mais illisible, ou un test plus simple mais clair — la carte invite à privilégier le second.
En pratique : Si un test est compréhensible par un Product Owner ou un développeur junior, c’est un bon indicateur de lisibilité. S’il nécessite 15 minutes d’explication, il n’est probablement pas Clean QA.
Comparaison avec d’autres approches
| Approche | Focus principal | Limite | Apport du Clean QA |
|---|---|---|---|
| TDD (Test Driven Development) | Conception par le test unitaire | Très technique, peu de vision métier | Apporte la lisibilité et la structure côté framework |
| BDD (Behavior Driven Development) | Scénarios lisibles par les métiers | Peut générer des steps mal factorisés | Applique des patterns pour éviter la dette QA |
| TestOps / CI-CD | Industrialisation et exécution massive | Souvent au détriment de la maintenabilité | Rappelle que chaque test doit être durable |
Anti-valeurs à éviter
- Écrire des tests uniquement pour « remplir la pyramide QA ».
- Optimiser la vitesse au détriment de la compréhension.
- Multiplier les frameworks hétérogènes sans cohérence.
- Laisser s’accumuler la dette technique en pensant « on refactorisera plus tard ».
En résumé, le Clean QA n’est pas une règle stricte à appliquer à la lettre, mais un guide de décision. Il invite les équipes à se poser la question : « Ce choix rend-il nos tests plus lisibles, plus durables, plus utiles ? ». Si la réponse est non, alors il est probablement temps de revoir l’approche.
Patterns appliqués aux tests
L’un des piliers de Clean QA est l’application des design patterns au monde du test. Ces schémas de conception, bien connus des développeurs, trouvent une pertinence toute particulière dans la construction de frameworks de QA robustes. Ils apportent clarté, réutilisabilité et modularité, permettant d’éviter la dette technique et de réduire la fragilité des tests.
« Sans patterns, les tests sont du code comme les autres… mais souvent en pire. Avec les patterns, ils deviennent un véritable langage de validation du système. »
Page Object Pattern
Le Page Object Pattern est sans doute le plus connu dans le domaine des tests UI. Son objectif est simple : séparer la logique d’interaction technique (sélecteurs, clics, saisies) des scénarios métier. Chaque page ou composant de l’application est représenté par une classe dédiée, ce qui permet d’éviter la duplication et d’améliorer la lisibilité.
Exemple simplifié en Java
class LoginPage {
void saisirIdentifiant(String user) {
driver.findElement(By.id("username")).sendKeys(user);
}
void saisirMotDePasse(String pwd) {
driver.findElement(By.id("password")).sendKeys(pwd);
}
void valider() {
driver.findElement(By.id("login")).click();
}
}
Avec ce pattern, les tests eux-mêmes deviennent plus expressifs :
LoginPage login = new LoginPage();
login.saisirIdentifiant("alice");
login.saisirMotDePasse("secret");
login.valider();
Screenplay Pattern
Le Screenplay Pattern, popularisé par la communauté Serenity BDD, va plus loin que le Page Object. Ici, les tests sont décrits comme une suite d’actions réalisées par un acteur. Chaque acteur possède des compétences (navigate, click, remember…), et exécute des tâches (login, search, purchase). Cela rapproche fortement le test du langage métier.
Exemple de scénario en pseudo-code
Actor alice = new Actor("Alice");
alice.can(BrowseTheWeb.with(driver));
alice.attemptsTo(
Login.withCredentials("alice", "secret"),
Search.forProduct("Laptop"),
AddToCart.theFirstResult()
);
Ici, le test se lit presque comme une user story. La lisibilité est exceptionnelle, et les détails techniques (locators, API calls) sont encapsulés dans les tâches.
Factory & Builder
Les tests automatisés reposent autant sur les données que sur les actions. Or, la duplication de datasets (articles, utilisateurs, commandes) est une source massive de dette technique. C’est là que les patterns Factory et Builder interviennent.
Exemple : génération d’un utilisateur de test
User user = UserBuilder.aUser()
.withName("Alice")
.withRole("Admin")
.withEmail("alice@test.com")
.build();
Avec ce pattern, créer de nouvelles données devient simple et expressif. Plus besoin de dupliquer des JSON ou des CSV, tout est centralisé. Le Factory peut fournir des objets standards (ex. un “utilisateur par défaut”), tandis que le Builder permet de les personnaliser.
Strategy & Observer
Certains tests nécessitent d’adapter leur comportement selon le contexte. Par exemple, une validation peut se faire via API en préproduction, mais via OCR en environnement POS. Le pattern Strategy permet de définir plusieurs stratégies de validation et de choisir dynamiquement laquelle utiliser.
Exemple simplifié : validation dynamique
interface ValidationStrategy {
boolean validate(Order order);
}
class ApiValidation implements ValidationStrategy {
boolean validate(Order order) { return api.check(order); }
}
class OcrValidation implements ValidationStrategy {
boolean validate(Order order) { return ocr.read(order.ticket); }
}
// Utilisation
ValidationStrategy strategy = new OcrValidation();
assertTrue(strategy.validate(order));
Le pattern Observer, quant à lui, est utile pour écouter les événements d’un test : capture de logs, screenshots, métriques de performance. Cela permet de brancher des outils de reporting ou d’analyse sans modifier la logique des tests.
Comparaison des patterns
| Pattern | Avantages | Limites | Quand l’utiliser |
|---|---|---|---|
| Page Object | Simplicité, encapsulation des sélecteurs | Devient lourd sur des projets complexes | Tests UI simples ou projets débutants |
| Screenplay | Lisibilité, réutilisabilité, langage métier | Courbe d’apprentissage, beaucoup de classes | Frameworks complexes, besoin de lisibilité |
| Factory / Builder | Factorisation des données, flexibilité | Demande une bonne conception du modèle | Gestion massive de datasets |
| Strategy | Adaptabilité, extensibilité | Complexité accrue si mal géré | Tests multi-contextes (API/UI/OCR) |
| Observer | Monitoring, reporting, audit | Risque de bruit excessif dans les logs | Frameworks avec exigence forte de traçabilité |
En résumé
L’application de ces patterns transforme radicalement un framework QA. Au lieu d’être un simple assemblage de scripts, il devient une architecture pensée, robuste et lisible. Chaque pattern répond à un besoin précis : structurer, réutiliser, adapter, monitorer. Utilisés ensemble, ils forment le socle de la démarche Clean QA.
Architecture Clean QA
Construire un framework de test automatisé, c’est comme construire une application critique : si les fondations sont fragiles, tout l’édifice s’écroule. L’architecture Clean QA part du principe que les tests doivent être conçus avec le même niveau de rigueur que le code de production. Pas de raccourcis, pas de bricolage : une stratification claire, une séparation des responsabilités, et une extensibilité naturelle.
« Les tests sont du code. Le nier, c’est condamner son framework QA à répéter les erreurs du passé. » — Adapté de Robert C. Martin (Clean Code)
Pourquoi une architecture ?
Beaucoup de projets QA échouent car ils ne considèrent pas le framework comme un logiciel à part entière. Résultat :
- Tests dispersés sans organisation claire.
- Dépendances implicites entre cas de test.
- Données dupliquées et non maintenues.
- Couplage fort avec l’outil (Selenium, Katalon, Cypress…).
Les couches principales de Clean QA
Un framework Clean QA repose sur une architecture en couches, chacune ayant une responsabilité unique.
1. Le Core
Le Core est la fondation. Il centralise la gestion des interactions génériques avec le système testé. Ici vivent les abstractions qui masquent les détails techniques.
public class ScreenOperationsManager {
public void clickOn(String locator) { ... }
public void enterText(String locator, String value) { ... }
public String readText(String locator) { ... }
}
Avantages :
- Un seul endroit à modifier si l’outil d’automatisation change.
- Réduction des duplications (clic, saisie, vérification toujours centralisés).
- Uniformité des logs et des erreurs.
2. Les Steps
Les Steps incarnent le métier. Chaque méthode correspond à une action fonctionnelle, en s’appuyant exclusivement sur le Core. Exemple :
public class CheckoutSteps {
private final ScreenOperationsManager screen;
public CheckoutSteps(ScreenOperationsManager screen) {
this.screen = screen;
}
public void ajouterArticleAuPanier(String ean) {
screen.enterText("#searchBar", ean);
screen.clickOn("#btnAdd");
}
}
3. Les Datasets
Les données sont le carburant des tests. Mal gérées, elles deviennent la source principale d’instabilité. En Clean QA, elles sont centralisées dans une couche dédiée : Datasets.
Exemple avec Builder
User user = UserBuilder.aUser()
.withName("Alice")
.withRole("Admin")
.withEmail("alice@test.com")
.build();
On distingue :
- Factories : fournissent des objets par défaut.
- Builders : permettent de créer des variations personnalisées.
- Sources dynamiques : API, BDD, fichiers JSON/CSV.
4. Les Utils
La boîte à outils transversale du framework. On y trouve les helpers (Logger, DateUtils, JsonParser…), réutilisables partout.
public class Logger {
public static void info(String message) { ... }
public static void error(String message, Exception e) { ... }
}
L’architecture hexagonale appliquée à la QA
Le modèle Ports & Adapters s’applique parfaitement à la QA. Les scénarios (le cœur métier) n’interagissent jamais directement avec Selenium, Appium ou une API. Ils passent par des ports, et les adapters traduisent la demande.
Exemple
interface PaymentPort {
boolean process(Payment payment);
}
class ApiPaymentAdapter implements PaymentPort {
public boolean process(Payment payment) { ... }
}
class UiPaymentAdapter implements PaymentPort {
public boolean process(Payment payment) { ... }
}
Comparaison : classique vs Clean QA
| Aspect | Framework classique | Framework Clean QA |
|---|---|---|
| Structure | Scripts dispersés, sélecteurs en dur | Modules stratifiés, responsabilités claires |
| Maintenance | Chaque changement casse des dizaines de tests | Un changement se gère dans le Core/Datasets |
| Évolutivité | Ajouts douloureux et coûteux | Facile à enrichir par ajout de Steps/Adapters |
| Lisibilité | Scénarios longs et techniques | Steps proches du langage métier |
Cas pratique : refactorisation d’un test spaghetti
Exemple d’un test classique écrit à la hâte :
// Mauvais exemple
driver.findElement(By.id("username")).sendKeys("alice");
driver.findElement(By.id("password")).sendKeys("secret");
driver.findElement(By.id("login")).click();
driver.findElement(By.id("search")).sendKeys("laptop");
driver.findElement(By.id("add")).click();
Version Clean QA après refactorisation :
// Steps + Core
LoginSteps login = new LoginSteps(screen);
CheckoutSteps checkout = new CheckoutSteps(screen);
login.seConnecter("alice", "secret");
checkout.ajouterArticleAuPanier("laptop");
Check-list Clean QA
Pour savoir si votre architecture est vraiment Clean QA, posez-vous ces questions :
- Les sélecteurs sont-ils centralisés (Core) ?
- Les Steps sont-ils 100% métier ?
- Les données sont-elles factorisées (Factories/Builders) ?
- Les dépendances externes sont-elles isolées par des Adapters ?
- Les Utils sont-ils découplés et réutilisables ?
- Peut-on changer d’outil (Selenium ➝ Playwright) sans réécrire tous les tests ?
En résumé
L’architecture Clean QA n’est pas une option, c’est un prérequis. Elle transforme la QA d’un centre de coûts fragile en un levier stratégique. En appliquant les principes de modularité, de stratification et d’isolation via Ports & Adapters, on obtient des tests :
- Robustes : ils survivent aux refactorings.
- Lisibles : ils parlent le langage du métier.
- Évolutifs : ils s’adaptent aux nouveaux besoins sans tout casser.
Bonnes pratiques Clean QA
Les bonnes pratiques constituent le garde-fou d’un framework Clean QA. Sans elles, même la meilleure architecture finit par se dégrader. Elles ne sont pas des options : ce sont des règles de survie. L’histoire de l’ingénierie logicielle montre que les projets échouent rarement par manque de fonctionnalités, mais très souvent par accumulation de dette technique. En QA, cette dette se manifeste par des tests instables, illisibles, coûteux à maintenir.
« Le code pourri fait échouer un projet. Les tests pourris font échouer une entreprise. »
1. Convention de nommage
Le nommage est la première barrière contre l’opacité. Un test doit être lu comme une **phrase claire**, reflétant le comportement validé. Bannissez les abréviations, les identifiants cryptiques, les versions numérotées.
// Mauvais
@Test
public void test_45_v2() { ... }
// Bon
@Test
public void authentification_echoue_si_mdp_invalide() { ... }
2. Assertions claires
Un test sans assertion n’est pas un test, c’est une simulation. Les assertions doivent être explicites et contextuelles.
// Mauvais
click("#submit");
// Bon
click("#submit");
assertTrue(isVisible("#confirmationMessage"),
"Le message de confirmation doit apparaître après la soumission.");
Les assertions trop génériques (“assertTrue(page.contains('OK'))”) sont à proscrire. Elles créent de faux positifs et dégradent la confiance.
3. Gestion des données
Les données sont le carburant de vos tests. Une mauvaise gestion entraîne : duplication, instabilité, dépendance à l’environnement. En Clean QA, on applique trois règles :
- Centralisation : une seule source de vérité pour chaque type de données.
- Abstraction : ne pas exposer directement les formats (CSV, SQL), mais passer par des Builders ou Factories.
- Flexibilité : générer dynamiquement quand c’est pertinent (utilisateur aléatoire, timestamp).
Exemple en Java
Product product = ProductBuilder.aProduct()
.withName("Laptop Gamer")
.withPrice(1499.90)
.withStock(50)
.build();
Résultat : plus de duplication. Chaque dataset est créé de manière contrôlée, et la maintenance devient triviale.
4. CI/CD et exécution massive
Les bonnes pratiques doivent survivre au passage à l’échelle. En CI/CD, les tests deviennent massifs, parallélisés, exécutés plusieurs fois par jour. Quelques règles :
- Indépendance : chaque test doit pouvoir tourner isolément.
- Parallélisation : éviter les dépendances globales (BDD partagée sans reset).
- Observabilité : logs, screenshots et métriques automatiques en cas d’échec.
- Idempotence : un test doit produire le même résultat, quelle que soit l’exécution.
5. Revues de code QA
Les tests sont du code, et doivent donc être soumis à review. Une bonne pratique Clean QA impose :
- Analyse des noms (clarté métier).
- Vérification de l’absence de duplication.
- Validation des assertions.
- Respect des patterns (Page Object, Screenplay, etc.).
6. Logs et reporting
Les logs QA doivent être orientés diagnostic. Pas de “Test échoué” générique. Un bon log dit quoi, où et pourquoi.
// Mauvais FAIL // Bon [CheckoutSteps] Échec : ajout d’article impossible Cause : stock insuffisant
7. Anti-patterns QA
Les mauvaises pratiques sont des bombes à retardement. En voici les plus courantes :
- Steps silencieux : clics sans vérification ➝ illusion de sécurité.
- Données en dur : duplication, instabilité.
- Dépendances cachées : test B ne marche que si test A a été exécuté.
- Assertions génériques : incapables de détecter une vraie régression.
- Ignorer un flaky test : “on le relancera” ➝ perte totale de confiance.
| Mauvaise pratique | Conséquence | Bonne pratique associée |
|---|---|---|
| Données en dur | Tests cassent à chaque changement | Centralisation via Builders/Factories |
| Assertions génériques | Faux positifs | Assertions explicites et contextuelles |
| Steps silencieux | Test non valide | Au moins une assertion par scénario |
Cas pratique : refactorisation
Exemple d’un test instable avant/après refactorisation :
// Mauvais
click("#login");
type("#user","alice");
type("#pwd","secret");
click("#ok");
assertTrue(page.contains("OK"));
// Bon
loginSteps.seConnecter("alice","secret");
assertTrue(dashboard.isVisible(),
"L’utilisateur doit accéder au tableau de bord après login.");
Quiz de validation
⚠️ Essayez de répondre par vous-même avant de regarder la solution. (Ne trichez pas ! Cliquez seulement si vous avez terminé 😉)
- Pourquoi un “step silencieux” est-il dangereux ?
- Citez 3 manières de centraliser vos datasets.
- Que doit contenir un log QA pour être utile ?
- Comment garantir l’indépendance des tests en CI/CD ?
- Donnez un exemple d’anti-pattern et sa contre-mesure.
Voir les réponses
- Un step silencieux est dangereux car il exécute une action sans vérifier le résultat ➝ faux sentiment de sécurité, incapacité à détecter une régression.
-
Trois manières de centraliser les datasets :
- Utiliser des Builders (flexibles, paramétrables).
- Passer par des Factories (valeurs par défaut).
- Brancher une source externe dynamique (BDD, API, CSV).
- Un log QA utile doit contenir : le contexte (où), l’action (quoi), le résultat attendu, et la cause si échec. Exemple : “Échec : ajout d’article impossible – stock insuffisant”.
-
L’indépendance des tests en CI/CD se garantit par :
- Création des données en setup à chaque test,
- Isolation des environnements (containers éphémères),
- Pas de dépendance entre tests.
- Exemple d’anti-pattern : utiliser des données en dur. Contre-mesure : centraliser via Builder/Factory pour éviter duplication et instabilité.
Exercice pratique
Refactorisez ce test spaghetti en Clean QA :
// Mauvais
driver.findElement(By.id("search")).sendKeys("Laptop");
driver.findElement(By.id("add")).click();
driver.findElement(By.id("checkout")).click();
Objectif : transformer en Steps métier + datasets centralisés + assertions explicites.
Checklist finale
- ✔️ Chaque test a une assertion claire.
- ✔️ Aucun dataset en dur.
- ✔️ Steps = métier, Core = technique.
- ✔️ Tests indépendants et parallélisables.
- ✔️ Logs orientés diagnostic.
En résumé
Les bonnes pratiques ne sont pas une option : ce sont des garanties de survie. Les ignorer, c’est condamner vos tests à devenir un poids mort. Les appliquer, c’est transformer la QA en un levier stratégique de confiance et de performance.