TDD vs BDD : Différences clés, une fonctionnalité conçue dans les deux sens et quand les utiliser
TDD aide les développeurs à concevoir un code correct. BDD aide toute une équipe à se mettre d’accord sur ce que le logiciel doit faire. Découvrez la même fonctionnalité de remise créée avec chaque approche et découvrez comment les deux fonctionnent ensemble.
- TDD signifie d’abord écrire un test qui a échoué, puis juste assez de code pour réussir, puis ranger.
- BDD décrit le comportement dans un langage partagé, généralement comme étant donné, quand, alors.
- TDD guide principalement la conception du code. BDD construit principalement une compréhension partagée.
- Ce ne sont pas des rivaux et de nombreuses équipes utilisent BDD pour les fonctionnalités et TDD pour les détails ci-dessous.
Le débat TDD contre BDD est souvent présenté comme un choix de camp. En réalité, ils répondent à des questions différentes. Le développement piloté par les tests demande si le code fait ce que le développeur voulait, tandis que le développement piloté par le comportement demande si l’équipe est d’accord sur ce que le logiciel devrait faire.
Pour rendre la différence concrète, nous construisons une petite fonctionnalité de remise pour chaque approche. Nous avons exécuté le code sur Python 3.13, en utilisant la bibliothèque standard, et les résultats ci-dessous proviennent de cette exécution. La partie BDD utilise notre propre petit coureur pour montrer le mécanisme, donc un vrai projet utiliserait un outil tel que Cucumber ou se comporterait à la place.
La réponse courte
Le développement piloté par les tests (TDD) est une pratique de codage. Vous écrivez un petit test qui échoue, le faites réussir avec le code le plus simple, puis améliorez la conception. Les tests sont écrits en code, et principalement par les développeurs. Kent Beck a popularisé cette pratique.
Le développement axé sur le comportement (BDD) est une pratique de collaboration issue du TDD, et Dan North a introduit le terme. Les développeurs, les testeurs et les professionnels s’accordent sur des exemples de comportement du système, rédigés en langage simple, et ces exemples peuvent ensuite être automatisés.
TDD vs BDD côte à côte
| Aspect | TDD | BDD |
|---|---|---|
| Objectif principal | Code correct et bien conçu | Compréhension partagée du comportement |
| Écrit dans | Langage de programmation | Langage simple, comme Gherkin |
| Écrit par | Développeurs | Entreprises, assurance qualité et développeurs ensemble |
| Lisible par | Développeurs | Tout le monde dans l’équipe |
| Niveau typique | Unité | Caractéristique ou acceptation |
| Point de départ | Un test raté | Une conversation avec des exemples |
| Cycle | Rouge, vert, refactoriser | Découvrir, formuler, automatiser |
| Outils communs | pytest, JUnit, plaisanterie | Concombre, comportez-vous, SpecFlow, Reqnroll |
La fonctionnalité
Voici la règle commerciale que nous mettrons en œuvre : Les commandes de 1 000 ou plus bénéficient d’une réduction de 10 %. Les membres bénéficient d’une réduction supplémentaire de 5 % sur ces commandes.
Cela peut paraître clair, mais c’est déjà ambigu. Un membre dépensant 500 bénéficie-t-il d’une réduction de 5 % ? La phrase dit « ces ordres », nous supposons donc que non. Gardez cela à l’esprit, car c’est exactement le genre de question que BDD est censé soulever dès le début.
Le construire avec TDD
TDD suit une courte boucle connue sous le nom de red, green, refactor :
| Étape | Ce que tu fais |
|---|---|
| Rouge | Écrivez un test pour un comportement qui n’existe pas encore et regardez-le échouer |
| Vert | Écrivez le code le plus simple qui le fasse réussir |
| Refactoriser | Améliorer le code en gardant les tests verts |
Voici nos tests, avec un pour chaque règle :
class DiscountTests(unittest.TestCase):
def test_no_discount_below_1000(self):
self.assertEqual(discount(999, False), 0)
def test_ten_percent_from_1000(self):
self.assertEqual(discount(1000, False), 100)
def test_member_gets_extra_five_percent(self):
self.assertEqual(discount(1000, True), 150)
Nous avons ensuite écrit le code en trois étapes, en exécutant les tests après chacune. Une session TDD stricte ajoute un test à la fois, et cela montre la même progression :
Étape 1 : le plus simple
def discount(total, is_member):
return 0
# 3 tests, 2 failed
# test_member_gets_extra_five_percent
# test_ten_percent_from_1000
Renvoyer zéro semble idiot, mais cela prouve que les tests peuvent en fait échouer, et le seul test réussi est la règle du « pas de remise ».
Étape 2 : ajouter le seuil
def discount(total, is_member):
if total >= 1000:
return total * 0.1
return 0
# 3 tests, 1 failed
# test_member_gets_extra_five_percent
Étape 3 : ajouter des membres et nettoyer
def discount(total, is_member):
rate = 0.1 if total >= 1000 else 0
if is_member:
rate += 0.05
return round(total * rate, 2)
# 3 tests, 0 failed
Tous les trois réussissent. Mais lisez attentivement cette version. Cela donne à un membre dépensant 500 $ une réduction de 5 %, ce qui n’est pas ce que nous supposions que la règle signifiait. Aucun test n’a couvert ce cas, donc rien ne l’a signalé. TDD ne protège que le comportement que vous pensiez tester. C’est une réelle limitation, et c’est la raison pour laquelle les conversations sur les exemples sont si importantes.
Pourquoi le cycle est important
- Le rouge prouve que le test fonctionne. Un test qui n’a jamais échoué pourrait ne rien tester.
- Le vert vous permet de rester concentré. Vous écrivez uniquement le code nécessaire, ce qui permet d’éviter une ingénierie excessive.
- Refactor maintient une qualité élevée. Les tests vous donnent la confiance nécessaire pour modifier la conception en toute sécurité.
- Cela influence la conception. Le code difficile à tester est souvent trop étroitement couplé, et vous le remarquez tout de suite.
Notre tutoriel pytest montre un moyen plus pratique d’écrire des tests comme ceux-ci, et l’analyse des valeurs limites explique pourquoi nous avons choisi 999 et 1 000 comme valeurs.
Le construire avec BDD
BDD commence par une conversation, pas par des outils. Il s’agit souvent d’une courte réunion connue sous le nom de trois amigos, avec un responsable produit, un développeur et un testeur discutant d’exemples concrets :
Tester: What about a member spending 500?
Product: Good question. No discount. It has to reach 1000 first.
Dev: And exactly 1000 counts?
Product: Yes, 1000 or more.
Cet échange aurait détecté le problème dans notre code TDD. Ils enregistrent ensuite leurs exemples dans un format structuré appelé Gherkin :
Feature: Order discounts
Scenario: Member reaches the discount threshold
Given a member with a cart worth 1000
When they view the amount to pay
Then the amount to pay is 850
Scenario: Member below the threshold gets no discount
Given a member with a cart worth 500
When they view the amount to pay
Then the amount to pay is 500
| Mot clé | Objectif |
|---|---|
| Étant donné | La situation de départ |
| Quand | L’action ou l’événement |
| Puis | Le résultat attendu |
N’importe quel membre de l’équipe peut lire cela et dire « oui, c’est vrai » ou « non, c’est faux ». Même si vous ne l’automatisez jamais, le scénario a déjà fait un travail utile.
Connecter les étapes au code
Les outils connectent chaque ligne en langage clair à une fonction, appelée définition d’étape. Pour montrer l’idée, nous avons écrit nous-mêmes une petite version et exécuté le premier scénario :
@step("a member with a cart worth 1000")
def given(ctx):
ctx["cart"] = Cart(is_member=True)
ctx["cart"].add(1000)
@step("they view the amount to pay")
def when(ctx):
ctx["amount"] = ctx["cart"].payable()
@step("the amount to pay is 850")
def then(ctx):
assert ctx["amount"] == 850
# ok Given a member with a cart worth 1000
# ok When they view the amount to pay
# ok Then the amount to pay is 850
De vrais outils ajoutent des paramètres, de sorte qu’une seule étape puisse correspondre à la fois à « 1 000 » et à « 500 », ainsi qu’à des rapports. Le résultat est une documentation évolutive : une description simple du système qui échoue bruyamment s’il cesse d’être vrai.
Astuce : La valeur du BDD réside dans la conversation. Les équipes qui ont une seule personne qui écrit Gherkin seule une fois le code terminé reçoivent le coût de l’outil, sans presque aucun avantage.
Écrire de bons scénarios
| Faible | Mieux |
|---|---|
Quand je clique sur le bouton avec l'identifiant "btn-77" |
Quand j'applique le coupon |
Alors le résultat est correct |
Le montant à payer est alors de 850 |
| Vingt étapes dans un scénario | Un comportement par scénario |
| Décrit l’écran | Décrit l’intention commerciale |
Les scénarios qui décrivent les clics et les identifiants de champs s’interrompent à chaque refonte, et les lecteurs professionnels arrêtent de les lire. Conservez les détails de l’interface utilisateur dans le code de l’étape. Ces scénarios sont de proches cousins des exemples de cas de test vs scénario de test, et ils sont également souvent utilisés dans les tests d’acceptation utilisateur.
Utiliser les deux ensemble
Les deux s’intègrent naturellement dans un flux de travail « de l’extérieur vers l’intérieur » :
- L’équipe se met d’accord sur le comportement et rédige un scénario BDD. Cela échoue, car rien n’existe encore.
- Un développeur utilise TDD pour construire les petites unités nécessaires.
- Les tests unitaires réussissent, tout comme le scénario.
- Tout s’exécute dans votre pipeline CI/CD.
Gardez chacun au bon niveau. Vous devriez disposer de quelques scénarios pour les comportements importants et de nombreux tests unitaires rapides pour les détails. Notre guide sur les tests unitaires et d’intégration couvre le reste.
Outils
| Langue | TDD (tests unitaires) | BDD |
|---|---|---|
| Python | pytest, test unitaire | comportez-vous, pytest-bdd |
| Javascript | Blague, Vitest | Cucumber.js |
| Java | JUnit, TestNG | Concombre-JVM |
| .NET | xUnit, NUnit | Reqnroll, SpecFlow |
Quand utiliser chacun
TDD s’adapte bien quand :
- Vous écrivez une logique complexe, comme la tarification, l’analyse ou les autorisations.
- Vous corrigez un bug, puisque vous pouvez le reproduire d’abord avec un test échoué.
- Vous travaillez sur du code qui changera souvent.
BDD convient bien quand :
- Les règles métier sont complexes et les malentendus coûtent cher.
- Les personnes non techniques liront et discuteront réellement des scénarios.
- Plusieurs équipes ont besoin d’une définition commune du comportement de quelque chose.
L’un ou l’autre peut être excessif lorsque : vous écrivez un prototype jetable ou explorez une idée technique peu claire. De nombreux développeurs commencent par commencer, puis réécrivent avec des tests. Les outils BDD constituent également un mauvais investissement si personne en dehors de l’ingénierie n’examine les scénarios. Dans ce cas, les tests simples portant de bons noms sont plus simples.
Erreurs courantes
- Traiter BDD comme un outil de test. Cucumber sans collaboration n’est qu’une façon verbeuse d’écrire des tests.
- Test d’implémentation dans TDD. Les tests liés aux détails privés sont interrompus à chaque refactorisation.
- Sauter l’étape de refactorisation. Vous obtenez des tests réussis en plus d’un code désordonné.
- Écrire des tests après le code et l’appeler TDD. Cela reste utile, mais vous perdez le retour sur la conception.
- Écrire trop de scénarios d’interface utilisateur. Ceux-ci deviennent lents et instables. Voir tests instables.
- À la recherche des chiffres de couverture. Une bonne couverture passe toujours à côté de comportements que personne n’a pris en compte, comme le montre notre exemple.
Pour conclure
TDD vous aide à construire la bonne chose, et BDD vous aide à construire la bonne chose. Notre exemple de réduction a montré les deux côtés : le code a réussi tous ses tests, mais une simple conversation a révélé une hypothèse erronée. Essayez-le sur votre prochaine petite fonctionnalité. Écrivez trois exemples concrets avec un collègue avant tout code, puis construisez-le un test échoué à la fois.
Questions fréquentes
Quelle est la principale différence entre TDD et BDD ?
TDD se concentre sur les développeurs qui écrivent des tests de code défaillants avant la mise en œuvre. BDD se concentre sur une équipe entière qui s’accorde sur un comportement à l’aide d’exemples en langage simple.
Pouvez-vous utiliser TDD et BDD ensemble ?
Oui, et de nombreuses équipes le font. Les scénarios BDD définissent les fonctionnalités de l’extérieur, et TDD gère les plus petites unités de code situées en dessous.
Le BDD est-il meilleur que le TDD ?
Ni l’un ni l’autre n’est globalement meilleur, car ils résolvent des problèmes différents. BDD aide à une compréhension partagée, et TDD aide à la conception de code et à un retour rapide.
Qu’est-ce que le cornichon ?
Gherkin est un format structuré en langage simple pour rédiger des scénarios, utilisant des mots-clés tels que Given, When et Then. Des outils comme Cucumber peuvent l’exécuter.
Le TDD ralentit-il le développement ?
Cela peut sembler plus lent au début, surtout pendant l’apprentissage. Les équipes gagnent souvent du temps grâce à moins de bugs et à des changements plus sûrs, bien que l’effet varie selon le projet.
Continuez à apprendre
Besoin d’aide pour les tests, WordPress ou le référencement ?
Nous aidons les équipes à proposer de meilleurs logiciels et à développer leurs sites. Dites-nous sur quoi vous travaillez et nous vous proposerons une prochaine étape claire.
Plus de guides sur tests, IA et développement moderne
Vous avez apprécié cet article ? Choisissez un sujet et accédez directement à nos nouveaux didacticiels.
Les hallucinations de l'IA expliquées : pourquoi les modèles de langage inventent les choses et comment les réduire
Les hallucinations de l'IA ont expliqué : ce qu'elles sont, pourquoi les modèles de langage les produisent, les types courants, comment les réduire …
Lire l'article →DevOpsVariables d'environnement expliquées : fichiers .env, secrets et configuration correctement effectués
Variables d'environnement expliquées avec des exemples Python que nous avons exécutés : lecture et définition, pourquoi chaque valeur est une chaîne…
Lire l'article →Développement logicielLimitation du débit de l'API expliquée : algorithmes, en-têtes et gestion des erreurs 429
Limitation du débit de l'API expliquée avec une simulation que nous avons exécutée : compartiment de jetons, fenêtre fixe, fenêtre glissante, 429 ré…
Lire l'article →Automatisation des testsSe moquer en Python : unittest.mock, patch et les erreurs qui font mentir les tests
Moquerie en Python avec unittest.mock, testée : Mock et return_value, side_effect, correctifs où un nom est utilisé, spec et autospec, dates moqueus…
Lire l'article →Tests logicielsCouverture du code expliquée : couverture des lignes par rapport aux branches et ce que signifie réellement 100 %
Couverture du code expliquée avec une véritable exécution de cover.py : couverture des lignes, des branches et des conditions, lecture d'un rapport,…
Lire l'article →Bases de données et SQLDELETE vs TRUNCATE vs DROP dans SQL : différences, restauration et quand les utiliser
DELETE vs TRUNCATE vs DROP expliqué avec SQL testé : ce que chacun supprime, restauration, déclencheurs, réinitialisation d'identité, vitesse et en …
Lire l'article →Bases de données et SQLVues SQL expliquées : créez, utilisez et évitez les pièges, avec des exemples
Vues SQL expliquées avec les requêtes que nous avons exécutées : création d'une vue, résultats en direct, masquage des colonnes, performances, vues …
Lire l'article →Développement logicielInterface TypeScript vs Type : différences réelles, testées avec le compilateur
Interface TypeScript vs type, vérifiée avec le vrai compilateur : ce que les deux peuvent faire, ce qu'un seul peut faire, la fusion des déclaration…
Lire l'article →Développement logicielPython *args et **kwargs expliqués : fonctions flexibles avec des exemples clairs
Python *args et **kwargs expliqués avec le code que nous avons exécuté : emballage et déballage, ordre des paramètres, arguments de mot-clé uniqueme…
Lire l'article →Développement logicielFonctions Python Lambda : syntaxe, utilisations réelles et quand ne pas les utiliser
Fonctions Python lambda expliquées avec le code que nous avons exécuté : syntaxe, tri avec clé, carte et filtre, fermetures, piège de liaison tardiv…
Lire l'article →Intelligence artificielleQue sont les agents IA ? Comment ils fonctionnent, avec une simple boucle d'agent que vous pouvez exécuter
Que sont les agents IA ? Un guide simple sur la boucle d'agent, les outils, la mémoire et les garde-fous, avec une petite boucle Python que nous avo…
Lire l'article →DevOpsSurveillance vs observabilité : la vraie différence, avec les journaux, les métriques et les traces
Surveillance et observabilité expliquées : échecs connus et inconnus, journaux, métriques et traces, présentation pas à pas des incidents de paiemen…
Lire l'article →DevOpsVolumes Docker vs montages Bind : conserver les données du conteneur de la bonne manière
Volumes Docker et montages de liaison expliqués : pourquoi les données du conteneur disparaissent, volumes nommés, montages de liaison, tmpfs, exemp…
Lire l'article →Automatisation des testsTDD vs BDD : principales différences, une fonctionnalité conçue dans les deux sens et quand les utiliser
TDD vs BDD expliqué avec une fonctionnalité de remise construite dans les deux sens : refactor rouge-vert avec le code que nous avons exécuté, scéna…
Lire l'article →Développement logicielGit merge vs rebase : différences, exemples et quand utiliser chacun
Git merge vs rebase expliqué avec de vraies sorties de log, un tableau comparatif, la règle d'or du rebase et un flux de travail sûr pour les équipes.
Lire l'article →Développement logicielList comprehension Python : syntaxe, exemples et cas à éviter
La list comprehension Python expliquée : syntaxe, filtres, if-else, boucles imbriquées, temps mesurés et cas où une boucle for classique est préféra…
Lire l'article →Bases de données et SQLJointures SQL expliquées avec exemples (Inner, Left, Right, Full)
Comprenez les jointures SQL pas à pas avec des tables et requêtes d'exemple : INNER, LEFT, RIGHT, FULL, CROSS et SELF, plus erreurs courantes et con…
Lire l'article →