Article 01·general·2 min de lecture
La pyramide des tests : pourquoi 70 % d'unitaires
Unit, intégration, E2E — trouver le bon ratio pour un vrai codebase.
La pyramide des tests, popularisée par Mike Cohn en 2009, recommande une distribution approximative de 70 % de tests unitaires, 20 % d'intégration, 10 % E2E. Beaucoup d'équipes inversent ce ratio par accident : elles écrivent surtout des tests E2E parce que "ça teste vraiment", et finissent avec une suite lente, fragile, et bizarrement peu protectrice contre les régressions réelles. Cet article explique pourquoi le ratio standard tient, et comment l'appliquer concrètement sur un projet en production.
Les tests unitaires sont rapides — moins de 10 ms par test, déterministes (pas de DB, pas de réseau, pas de FS hors mock), et exécutés à chaque sauvegarde de fichier en mode watch. Ils protègent les invariants métier au niveau de la fonction ou de la classe : "cette fonction de scoring retourne 0 pour une main vide, 21 pour un blackjack, etc." Sur SallyCards, la suite unitaire couvre 1 240 tests, tourne en 12 secondes, et est lancée par un hook Husky pre-push. Coût marginal d'un nouveau test : ~3 minutes d'écriture.
Les tests d'intégration vérifient les frontières du système. "Le service UsersService.create() appelle Mongoose correctement et retourne l'objet hydraté." Ils utilisent une vraie base (mongodb-memory-server) ou un container Docker éphémère, durent 50-500 ms chacun, et tournent sur chaque commit en CI. Sur SallyCards : 84 tests, ~45 secondes. Le ratio est faible parce qu'on capture l'essentiel des bugs d'intégration via les contrats Pact (consumer-driven contracts) entre le mobile et l'API.
Les tests E2E (end-to-end) reproduisent un parcours utilisateur complet : "ouvre l'app, login, crée un match, joue 5 cartes, vérifie le score." Ils sont LENTS (3-15 secondes par test) et CASSANTS (changement d'un sélecteur CSS = test rouge). Leur valeur unique : ils attrapent les bugs d'intégration entre couches qui n'apparaissent dans aucun test isolé — par exemple un mismatch de schéma entre le validateur Joi côté API et le type TypeScript côté mobile. Sur SallyCards : 11 happy-paths critiques (un par jeu), 6 min total en parallèle.
La règle empirique : si un test peut être écrit comme unitaire SANS perdre de couverture utile, écris-le unitaire. Si ça nécessite vraiment 3 modules qui parlent ensemble, c'est de l'intégration. Si ça nécessite l'app complète en marche, c'est E2E. Et reste rigoureux : un test "unitaire" qui mock 12 dépendances n'est plus unitaire, c'est de l'intégration déguisée — souvent moins lisible qu'un vrai test d'intégration avec une vraie base.
Comment migrer une suite déséquilibrée ? D'abord, mesure : `jest --coverage` sur tes specs unitaires donne le baseline. Ensuite, pour chaque test E2E lent, demande "puis-je extraire un test unitaire qui valide le bug que ce E2E protège ?" Souvent oui — le E2E reste utile mais devient redondant pour la régression principale. Sur 6 mois, on a réduit la suite E2E de 84 à 11 tests, tout en augmentant la couverture totale de 71 % à 87 %. Le temps de CI total est passé de 32 minutes à 7 minutes.
Anti-pattern courant : la "pyramide inversée" (Ice Cream Cone). C'est ce qu'on obtient quand l'équipe est dirigée par une obsession "tests utilisateur" sans gouvernance. Le coût caché est massif : un changement de design (renommer un bouton) casse 20 tests E2E sans bug réel sous-jacent. Symptôme : les développeurs commencent à désactiver les tests "pour faire passer le PR" — c'est le début de la fin.
theoryCI/CDpyramid