Les tests automatises sont devenus un standard de l'industrie. Plus personne ne conteste serieusement leur utilite. Mais des qu'on entre dans le detail, un debat emerge : faut-il ecrire les tests avant le code ? Apres ? Avec le metier ?
TDD, BDD, tests a posteriori — chaque approche a ses defenseurs passionnes et ses detracteurs tout aussi convaincus. La realite est plus nuancee que les positions dogmatiques. Chaque strategie a sa place, et les meilleures equipes les combinent en fonction du contexte.
Cet article passe en revue les trois approches sans parti pris, analyse leurs forces et limites respectives, et propose une strategie de combinaison adaptee aux projets reels.
TDD : ecrire le test avant le code
Le Test-Driven Development (TDD) suit un cycle strict en trois etapes, popularise par Kent Beck : red, green, refactor.
- Red — ecrire un test qui echoue. Le test decrit le comportement attendu d'un morceau de code qui n'existe pas encore.
- Green — ecrire le minimum de code necessaire pour que le test passe. Pas d'optimisation, pas de generalisation. Juste assez pour passer au vert.
- Refactor — nettoyer le code sans changer le comportement. Le test vert garantit qu'on ne casse rien.
Ce cycle se repete en boucles courtes — parfois toutes les deux minutes. Le test guide la conception. Le code emerge du besoin, pas d'une architecture imaginee a l'avance.
Quand le TDD brille
Le TDD donne ses meilleurs resultats sur la logique metier complexe. Algorithmes de calcul, regles de tarification, moteurs de validation, transformations de donnees — tout ce qui a des entrees, des sorties et des regles precises.
Dans ces contextes, le TDD force a clarifier le comportement attendu avant d'ecrire la moindre ligne d'implementation. C'est un outil de conception autant qu'un outil de test. Le code resultant est naturellement decouple, car il a ete ecrit pour etre testable des le depart.
Le TDD excelle aussi dans les situations ou les cas limites sont nombreux. Chaque cas limite devient un test explicite. On ne les decouvre pas en production — on les specifie a l'avance.
Forces
- Feedback immediat — chaque changement est valide en secondes.
- Conception emergente — le code est structure par les besoins reels, pas par des abstractions prematurees.
- Documentation executable — les tests decrivent exactement ce que fait le code, avec des exemples concrets.
- Confiance dans le refactoring — on peut restructurer le code sans crainte tant que les tests passent.
Limites
- Cout d'apprentissage eleve — le TDD demande une discipline qui s'acquiert avec la pratique. Les premieres semaines sont frustantes : on a l'impression d'aller plus lentement.
- Inadapte a l'exploration — quand on ne sait pas encore a quoi ressemblera la solution (prototype, POC, spike technique), ecrire des tests avant le code est contre-productif.
- Difficulte sur le code a effets de bord — les interactions avec une base de donnees, une API externe ou un systeme de fichiers requierent des mocks ou des doubles de test qui alourdissent le cycle.
- Risque de sur-specification — des tests trop couples a l'implementation empechent le refactoring au lieu de le faciliter.
BDD : specifier par l'exemple
Le Behavior-Driven Development (BDD) est ne d'un constat de Dan North : le TDD, malgre ses qualites, ne resout pas le probleme de communication entre developpeurs et metier. Les tests techniques sont illisibles pour les parties prenantes non techniques.
Le BDD propose d'ecrire les specifications dans un langage structure mais lisible par tous : le format Gherkin.
Le format Given/When/Then
Chaque scenario BDD suit une structure en trois parties :
- Given (etant donne) — le contexte initial, l'etat du systeme avant l'action.
- When (quand) — l'action declenchee par l'utilisateur ou le systeme.
- Then (alors) — le resultat attendu, l'etat du systeme apres l'action.
Un scenario Gherkin ressemble a ceci : « Etant donne un utilisateur avec un panier de 3 articles, quand il applique un code promo de 20 %, alors le total affiche est reduit de 20 % et le code promo apparait dans le recapitulatif. »
Ce scenario est a la fois une specification metier,
un critere d'acceptance et un test automatise.
Les outils BDD comme Behat (PHP) ou Cucumber (multi-langage)
executent ces scenarios directement.
Quand le BDD brille
Le BDD est particulierement efficace dans les projets multi-parties prenantes ou la communication entre metier et technique est un enjeu.
Quand les specifications sont ambigues — « l'utilisateur doit pouvoir gerer ses abonnements » — le BDD force a les concretiser par des exemples. Chaque scenario elimine une ambiguite. Le resultat est une specification vivante, toujours a jour car executee a chaque build.
Le BDD excelle aussi pour les criteres d'acceptance. Au lieu de debattre de ce que « termine » signifie, l'equipe ecrit les scenarios qui valident la fonctionnalite. Quand tous les scenarios passent, la fonctionnalite est terminee. C'est une definition concrete, verifiable, automatisee.
Forces
- Langage partage — les scenarios sont lisibles par les developpeurs, les product owners et les testeurs.
- Specifications vivantes — les scenarios Gherkin sont a la fois documentation et tests executables.
- Reduction des malentendus — specifier par l'exemple elimine les interpretations divergentes.
- Couverture fonctionnelle — les tests BDD verifient le comportement du point de vue de l'utilisateur, pas de l'implementation.
Limites
- Cout de maintenance des scenarios — les scenarios Gherkin peuvent proliferer et devenir un fardeau a maintenir si l'equipe n'est pas disciplinee.
-
Tentation du « Gherkin everywhere » — tout n'a pas besoin
d'etre specifie en Gherkin. Les regles de calcul pures
sont mieux testees en unitaire avec
PHPUnit. - Adoption organisationnelle — le BDD ne fonctionne que si le metier participe a l'ecriture des scenarios. Sans cette collaboration, on se retrouve avec des developpeurs qui ecrivent du Gherkin pour eux-memes — ce qui en annule l'interet.
- Performance des tests — les scenarios BDD traversent toute la pile applicative. Ils sont plus lents que des tests unitaires et plus fragiles face aux changements d'UI.
Tests a posteriori : le pragmatisme assume
Ecrire les tests apres le code est souvent presente comme une pratique inferieure — un compromis regrettable face aux contraintes du monde reel. C'est une vision reductrice.
Les tests a posteriori sont une strategie legitime qui, dans certains contextes, est non seulement acceptable mais preferable.
Quand c'est le bon choix
Les projets legacy sans tests. Quand on herite d'une application de 100 000 lignes sans un seul test, le debat TDD vs BDD est academique. L'urgence est d'ajouter des tests de caracterisation pour securiser le code existant. Ces tests sont necessairement a posteriori — le code est deja ecrit depuis des annees.
Le prototypage et l'exploration technique. Quand on explore une nouvelle API, un nouveau framework ou une approche architecturale inconnue, ecrire des tests avant le code est premature. On ne sait pas encore quelles seront les bonnes abstractions. Mieux vaut explorer, stabiliser, puis tester.
Le code d'interface utilisateur. Les composants visuels, les animations, le responsive — ce code se valide visuellement, pas via des assertions programmatiques. Les tests snapshot ou les tests de regression visuelle sont par nature des tests a posteriori.
L'exploration technique (spike). Un spike est un investissement en connaissance, pas en code. Le code produit pendant un spike est jetable. Le tester serait une perte de temps. Les tests viendront quand la solution sera stabilisee.
Forces
- Aucune barriere d'entree — pas besoin de maitriser le TDD ou le BDD pour commencer a tester.
- Flexibilite — on peut tester a n'importe quel moment, sur n'importe quel code, sans contrainte de processus.
- Adapte au legacy — c'est la seule approche viable pour couvrir du code existant.
Limites
- Pas d'effet sur la conception — les tests a posteriori ne guident pas l'architecture du code. Le code est deja ecrit, pour le meilleur et pour le pire.
- Risque de « tester l'implementation » — quand on ecrit un test en regardant le code, on a tendance a reproduire sa logique dans le test au lieu de verifier son comportement.
- Discipline requise — sans le cadre impose par le TDD, il est facile de « reporter les tests a plus tard ». Et « plus tard » arrive rarement.
Comparaison sur 5 axes
Pour choisir entre ces trois approches, il est utile de les comparer sur les criteres qui comptent en pratique.
Cout d'adoption
Les tests a posteriori ont le cout d'adoption le plus bas : il suffit de savoir ecrire un test. Le BDD demande un investissement organisationnel significatif (formation, outillage, collaboration metier/technique). Le TDD se situe entre les deux : le cout est surtout individuel (discipline, pratique), mais il ne necessite pas de reorganisation de l'equipe.
Boucle de feedback
Le TDD offre la boucle la plus courte : quelques secondes entre l'ecriture d'un test et sa validation. Les tests a posteriori ont une boucle variable, selon le moment ou ils sont ecrits. Le BDD a la boucle la plus longue, car les scenarios traversent toute la pile applicative.
Documentation vivante
Le BDD produit la meilleure documentation : les scenarios Gherkin sont lisibles par tous et toujours a jour. Le TDD produit une documentation technique de qualite, mais reservee aux developpeurs. Les tests a posteriori documentent le comportement actuel, mais manquent souvent de la clarte intentionnelle des tests ecrits en amont.
Capacite de refactoring
Le TDD et le BDD permettent un refactoring en confiance, car les tests existent avant toute modification. Les tests a posteriori protegent aussi le refactoring, mais avec un risque : si les tests sont couples a l'implementation (ce qui arrive plus souvent quand on teste apres), ils freinent le refactoring au lieu de le faciliter.
Couverture
Les tests a posteriori offrent la couverture la plus flexible : on peut cibler n'importe quelle partie du code. Le TDD garantit une couverture intrinseque — tout code ecrit est couvert, car il a ete ecrit pour satisfaire un test. Le BDD couvre les parcours fonctionnels de bout en bout, mais laisse des zones non couvertes dans la logique interne.
La combinaison optimale par couche
La question n'est pas « TDD ou BDD ou tests a posteriori ? » La question est : quelle approche pour quel type de code ?
Une strategie de test mature combine les trois approches, chacune appliquee la ou elle apporte le plus de valeur.
Domaine et logique metier : TDD
La logique metier pure — calculs, validations, regles, transformations de donnees — est le terrain de predilection du TDD. Le code est sans effets de bord, les entrees et sorties sont claires, et la specification est precise.
Le TDD force a decoupler cette logique du reste de l'application. Le resultat : des classes metier testables, reutilisables et independantes du framework.
Use cases et services : tests d'integration a posteriori
Les services applicatifs orchestrent la logique metier, accedent a la base de donnees, appellent des APIs externes. Les tester en isolation pure (avec des mocks partout) produit des tests fragiles qui ne detectent pas les vrais problemes.
Les tests d'integration a posteriori sont plus adaptes : on teste le service avec ses dependances reelles, dans un environnement proche de la production. On ecrit ces tests une fois que l'implementation est stabilisee.
Controllers et UI : BDD ou tests E2E
Les controllers et les interfaces utilisateur n'ont pas de logique metier (ou ne devraient pas en avoir). Ils assemblent des composants et orchestrent des flux. Les tester unitairement est rarement utile.
Le BDD est ideal pour cette couche :
les scenarios decrivent le parcours utilisateur
de bout en bout, dans un langage metier.
Alternativement, des tests E2E avec Playwright
ou Cypress couvrent les memes besoins
sans le formalisme Gherkin.
Infrastructure : tests de contrat
Les adaptateurs d'infrastructure (clients HTTP, repositories, connecteurs de messagerie) doivent respecter un contrat defini par l'application. Les tests de contrat verifient que l'adaptateur respecte ce contrat sans tester toute la logique applicative au-dessus.
Ces tests sont typiquement ecrits a posteriori, une fois que le contrat est stabilise. Ils protegent contre les regressions lors des mises a jour de librairies ou de changements d'API externes.
Plan d'adoption progressif
La pire erreur est d'imposer le TDD a toute l'equipe du jour au lendemain. C'est le meilleur moyen de generer de la frustration, de ralentir les livraisons et de decredibiliser la demarche.
Une adoption progressive est bien plus efficace.
Phase 1 : les tests a posteriori comme socle
Commencer par l'approche la plus accessible. Definir une regle simple : tout bug corrige s'accompagne d'un test de non-regression. C'est une habitude facile a prendre, qui produit des resultats immediats.
En parallele, couvrir les parcours critiques avec des tests E2E. L'objectif n'est pas la couverture maximale — c'est d'avoir un filet de securite qui detecte les regressions majeures.
Phase 2 : introduire le TDD sur les nouveaux modules critiques
Une fois que l'equipe est a l'aise avec les tests, introduire le TDD sur les nouveaux developpements critiques. Pas sur tout — sur la logique metier complexe, la ou les cas limites sont nombreux et les erreurs couteuses.
Accompagner cette introduction par du pair programming ou du mob programming. Le TDD s'apprend en pratiquant avec quelqu'un qui maitrise la discipline. Les formations theoriques ne suffisent pas.
Phase 3 : BDD pour les user stories complexes
Quand l'equipe a un bon niveau de maturite en tests, introduire le BDD sur les fonctionnalites ou la collaboration metier/technique est critique.
Organiser des ateliers « Example Mapping » avec les developpeurs et les product owners. Chaque user story complexe est decomposee en exemples concrets qui deviennent les scenarios Gherkin.
Ne pas generaliser le BDD a toutes les stories. Le reserver aux fonctionnalites ou les specifications sont ambigues, ou les regles metier sont complexes, ou plusieurs parties prenantes ont des attentes divergentes.
Phase 4 : affiner et optimiser
Avec l'experience, l'equipe developpe un sens intuitif de l'approche adaptee a chaque situation. Le developpeur senior sait qu'un calcul de commission merite du TDD, qu'un CRUD basique se teste a posteriori et qu'un parcours d'onboarding complexe beneficie du BDD.
A ce stade, les trois approches coexistent naturellement. L'equipe ne debat plus de « TDD vs BDD » — elle choisit l'outil adapte au probleme.
Ce que les tests ne remplacent pas
Quelle que soit la strategie adoptee, les tests automatises ne sont qu'un element d'une demarche qualite plus large.
Ils ne remplacent pas la revue de code, qui detecte des problemes de lisibilite, de convention et de conception qu'aucun test ne peut capturer.
Ils ne remplacent pas l'analyse statique
(PHPStan, Psalm),
qui detecte des categories de bugs
que les tests manquent (types incorrects,
variables indefinies, code mort).
Et ils ne remplacent pas une architecture saine. Des tests sur du code mal structure sont des tests fragiles. La meilleure strategie de test commence par une reduction de la dette technique qui rend le code structurellement testable.
Choisir sa strategie
Il n'y a pas de reponse universelle a la question « TDD, BDD ou tests a posteriori ? ». La bonne reponse depend du contexte : le type de code, la maturite de l'equipe, la nature du projet, les contraintes organisationnelles.
Ce qui compte, c'est d'avoir une strategie. Un projet sans strategie de test finit toujours de la meme maniere : avec une base de code que personne n'ose toucher.
Chez x10, nous aidons les equipes a definir et mettre en oeuvre une strategie de test adaptee a leur contexte. Que ce soit dans le cadre d'un developpement sur mesure ou d'un audit technique d'un projet existant, nous privilegions le pragmatisme : les bonnes pratiques au bon endroit, pas le dogmatisme. Echangeons sur votre projet.