TDD versus BDD: Belangrijkste verschillen, één functie die op beide manieren is gebouwd en wanneer deze moet worden gebruikt
TDD helpt ontwikkelaars bij het ontwerpen van correcte code. BDD helpt een heel team het eens te worden over wat de software moet doen. Bekijk dezelfde kortingsfunctie die bij elke aanpak is toegepast en leer hoe deze twee samenwerken.
- TDD betekent dat je eerst een mislukte test schrijft, dan net genoeg code om te slagen en dan opruimt.
- BDD beschrijft gedrag in gedeelde taal, meestal als Gegeven, Wanneer, Dan.
- TDD begeleidt voornamelijk het codeontwerp. BDD bouwt vooral aan gedeeld begrip.
- Ze zijn geen rivalen en veel teams gebruiken BDD voor functies en TDD voor de onderstaande details.
Het debat TDD versus BDD wordt vaak voorgesteld als het kiezen van een kant. In werkelijkheid beantwoorden ze verschillende vragen. Bij testgedreven ontwikkeling wordt gevraagd of de code doet wat de ontwikkelaar bedoelde, terwijl bij gedragsgedreven ontwikkeling wordt gevraagd of het team het überhaupt eens is over wat de software moet doen.
Om het verschil concreet te maken, bouwen we bij elke aanpak één kleine kortingsfunctie. We hebben de code uitgevoerd op Python 3.13, met behulp van de standaardbibliotheek, en de onderstaande resultaten komen uit die run. Het BDD-gedeelte gebruikt een eigen klein lopertje om het mechanisme te laten zien, dus een echt project zou een hulpmiddel zoals Komkommer gebruiken of zich in plaats daarvan gedragen.
Het korte antwoord
Testgestuurde ontwikkeling (TDD) is een codeerpraktijk. Je schrijft een kleine mislukte test, zorgt ervoor dat deze slaagt met de eenvoudigste code en verbetert vervolgens het ontwerp. Tests worden in code geschreven, en voornamelijk door ontwikkelaars. Kent Beck maakte de praktijk populair.
Gedragsgestuurde ontwikkeling (BDD) is een samenwerkingspraktijk die is voortgekomen uit TDD, en Dan North introduceerde de term. Ontwikkelaars, testers en zakenmensen zijn het eens over voorbeelden van hoe het systeem zich zou moeten gedragen, geschreven in duidelijke taal, en die voorbeelden kunnen vervolgens worden geautomatiseerd.
TDD versus BDD naast elkaar
| Aspect | TDD | BDD |
|---|---|---|
| Hoofdfocus | Correcte, goed ontworpen code | Gedeeld begrip van gedrag |
| Ingeschreven | Programmeertaal | Duidelijke taal, zoals augurk |
| Geschreven door | Ontwikkelaars | Business, QA en ontwikkelaars samen |
| Leesbaar door | Ontwikkelaars | Iedereen in het team |
| Typisch niveau | Eenheid | Kenmerk of acceptatie |
| Uitgangspunt | Een mislukte proef | Een gesprek met voorbeelden |
| Cyclus | Rood, groen, refactor | Ontdek, formuleer, automatiseer |
| Gemeenschappelijke hulpmiddelen | pytest, JUnit, Jest | Komkommer, gedragen, SpecFlow, Reqnroll |
De functie
Dit is de bedrijfsregel die we zullen implementeren: Bestellingen van 1000 of meer krijgen 10% korting. Leden krijgen 5% extra korting op deze bestellingen.
Dat klinkt misschien helder, maar het is al dubbelzinnig. Krijgt een lid dat 500 euro besteedt, 5% korting? In de zin staat ‘die bevelen’, dus we gaan ervan uit van niet. Houd dat in gedachten, want dit is precies het soort vraag dat BDD in een vroeg stadium naar voren wil brengen.
Bouw het met TDD
TDD volgt een korte lus die bekend staat als rood, groen, refactor:
| Stap | Wat je doet |
|---|---|
| Rood | Schrijf één test voor gedrag dat nog niet bestaat, en zie hoe het mislukt |
| Groen | Schrijf de eenvoudigste code die ervoor zorgt dat het lukt |
| Refactor | Verbeter de code en houd de tests groen |
Dit zijn onze tests, met één voor elke regel:
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)
Vervolgens hebben we de code in drie fasen geschreven, waarbij we na elke fase de tests hebben uitgevoerd. Een strikte TDD-sessie voegt één test tegelijk toe, en dit laat dezelfde progressie zien:
Fase 1: het eenvoudigste
def discount(total, is_member):
return 0
# 3 tests, 2 failed
# test_member_gets_extra_five_percent
# test_ten_percent_from_1000
Het retourneren van nul ziet er raar uit, maar het bewijst dat de tests daadwerkelijk kunnen mislukken, en de enige test die het doorstaat is de ‘geen korting’-regel.
Fase 2: voeg de drempel toe
def discount(total, is_member):
if total >= 1000:
return total * 0.1
return 0
# 3 tests, 1 failed
# test_member_gets_extra_five_percent
Fase 3: leden toevoegen en opruimen
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
Alle drie passeren. Maar lees deze versie aandachtig. Het geeft een lid dat 500 euro besteedt een korting van 5%, wat niet is wat we dachten dat de regel betekende. Geen enkele test heeft dat geval gedekt, dus niets heeft dit gemarkeerd. TDD beschermt alleen het gedrag dat je dacht te testen. Dat is een echte beperking, en de reden waarom gesprekken over voorbeelden zo belangrijk zijn.
Waarom de cyclus ertoe doet
- Rood bewijst dat de test werkt. Een test die nooit heeft gefaald, test misschien niets.
- Groen houdt je gefocust. Je schrijft alleen de code die nodig is, waardoor over-engineering wordt voorkomen.
- Refactor houdt de kwaliteit hoog. Tests geven u het vertrouwen om het ontwerp veilig te wijzigen.
- Het heeft invloed op het ontwerp. Moeilijk te testen code is vaak te nauw met elkaar verbonden, en dat merk je meteen.
Onze pytest-tutorial laat een gemakkelijkere manier zien om dit soort tests te schrijven, en grenswaardeanalyse legt uit waarom we 999 en 1000 als waarden hebben gekozen.
Bouw het met BDD
BDD begint met een gesprek, niet met hulpmiddelen. Vaak is het een korte bijeenkomst die bekend staat als de drie amigo’s, waarbij een productpersoon, een ontwikkelaar en een tester concrete voorbeelden bespreken:
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.
Die ene uitwisseling zou het probleem in onze TDD-code hebben opgevangen. Vervolgens nemen ze hun voorbeelden op in een gestructureerd formaat genaamd 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
| Trefwoord | Doel |
|---|---|
| Gegeven | De uitgangssituatie |
| Wanneer | De actie of gebeurtenis |
| Dan | Het verwachte resultaat |
Iedereen in het team kan dat lezen en zeggen ‘ja, dat klopt’ of ‘nee, dat klopt’. Zelfs als je het nooit automatiseert, heeft het scenario al nuttig werk verricht.
Stappen verbinden met code
Tools verbinden elke regel in duidelijke taal met een functie, een zogenaamde stapdefinitie. Om het idee te laten zien, hebben we zelf een kleine versie geschreven en het eerste scenario uitgevoerd:
@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
Echte tools voegen parameters toe, zodat een enkele stap zowel “1000” als “500” kan matchen, plus rapportage. Het resultaat is levende documentatie: een duidelijke beschrijving van het systeem, die luid faalt als het niet meer waar is.
Tip: De waarde van BDD ligt in het gesprek. Teams waarbij één persoon Gherkin alleen schrijft nadat de code is voltooid, krijgen de kosten van de tool, maar vrijwel niets van de voordelen.
Goede scenario’s schrijven
| Zwak | Beter |
|---|---|
Wanneer ik op de knop met id "btn-77" klik |
Wanneer ik de coupon toepas |
Dan klopt de uitslag |
Dan bedraagt het te betalen bedrag 850 |
| Twintig stappen in één scenario | Eén gedrag per scenario |
| Beschrijft het scherm | Beschrijft de zakelijke bedoeling |
Scenario’s die klikken en veld-ID’s beschrijven, breken bij elk nieuw ontwerp en zakelijke lezers lezen ze niet meer. Bewaar de details van de gebruikersinterface in de stapcode. Deze scenario’s zijn nauw verwant aan de voorbeelden in testcase versus testscenario, en worden ook vaak gebruikt bij gebruikersacceptatietests.
Beide samen gebruiken
De twee passen op natuurlijke wijze in een ‘outside-in’-workflow:
- Het team maakt afspraken over het gedrag en schrijft een BDD-scenario. Het mislukt, omdat er nog niets bestaat.
- Een ontwikkelaar gebruikt TDD om de benodigde kleine eenheden te bouwen.
- Unit-tests slagen, en uiteindelijk ook het scenario.
- Alles draait in uw CI/CD-pijplijn.
Houd ze allemaal op het juiste niveau. Je zou een paar scenario’s moeten hebben voor belangrijk gedrag, en veel snelle unit-tests voor de details. Onze gids voor unit- versus integratietests behandelt de balans.
Gereedschap
| Taal | TDD (eenheidstesten) | BDD |
|---|---|---|
| Python | pytest, unittest | gedragen, pytest-bdd |
| JavaScript | Gek, Vitest | Cucumber.js |
| Java | JUnit, TestNG | Komkommer-JVM |
| .NET | xEenheid, NUeenheid | Reqnroll, SpecFlow |
Wanneer moet u ze allemaal gebruiken?
TDD past goed wanneer:
- U schrijft complexe logica, zoals prijzen, parseren of machtigingen.
- Je repareert een bug, omdat je deze eerst kunt reproduceren met een mislukte test.
- Je werkt aan code die vaak zal veranderen.
BDD past goed wanneer:
- Bedrijfsregels zijn complex en misverstanden zijn duur.
- Niet-technische mensen zullen de scenario’s echt lezen en bespreken.
- Verschillende teams hebben behoefte aan een gedeelde definitie van hoe iets zich gedraagt.
Het kan overdreven zijn als je: een wegwerpprototype schrijft of een onduidelijk technisch idee onderzoekt. Veel ontwikkelaars pieken eerst en herschrijven vervolgens met tests. BDD-tooling is ook een slechte investering als niemand buiten de engineering ooit naar de scenario’s zal kijken. In dat geval zijn eenvoudige tests met goede namen eenvoudiger.
Veel voorkomende fouten
- BDD behandelen als een testtool. Komkommer zonder samenwerking is slechts een uitgebreide manier om tests te schrijven.
- Implementatie testen in TDD. Tests die verband houden met privégegevens breken tijdens elke refactor.
- De refactorstap wordt overgeslagen. Je slaagt voor tests naast rommelige code.
- Tests achter de code schrijven en deze TDD noemen. Dat is nog steeds nuttig, maar je verliest de ontwerpfeedback.
- Te veel UI-scenario’s schrijven. Deze worden traag en onstabiel. Zie flaky-tests.
- Op jacht naar dekkingscijfers. Goede berichtgeving mist nog steeds gedrag waar niemand rekening mee heeft gehouden, zoals uit ons voorbeeld blijkt.
Tot slot
TDD helpt je om het juiste te bouwen, en BDD helpt je om het juiste te bouwen. Ons kortingsvoorbeeld liet beide kanten zien: de code doorstond alle tests, maar een eenvoudig gesprek bracht een verkeerde veronderstelling aan het licht. Probeer het op uw volgende kleine functie. Schrijf samen met een collega drie concrete voorbeelden vóór elke code, en bouw deze vervolgens één mislukte test tegelijk.
Veelgestelde vragen
Wat is het belangrijkste verschil tussen TDD en BDD?
TDD richt zich op ontwikkelaars die falende codetests schrijven vóór implementatie. BDD richt zich erop dat een heel team het eens wordt over gedrag aan de hand van voorbeelden in duidelijke taal.
Kun je TDD en BDD samen gebruiken?
Ja, en veel teams doen dat ook. BDD-scenario’s definiëren functies van buitenaf, en TDD stuurt de kleinere code-eenheden eronder aan.
Is BDD beter dan TDD?
Geen van beide is in het algemeen beter, omdat ze verschillende problemen oplossen. BDD helpt bij gedeeld begrip, en TDD helpt bij codeontwerp en snelle feedback.
Wat is augurk?
Augurk is een gestructureerd formaat in eenvoudige taal voor het schrijven van scenario’s, met behulp van trefwoorden als Gegeven, Wanneer en Dan. Tools zoals Cucumber kunnen het uitvoeren.
Vertraagt TDD de ontwikkeling?
In het begin kan het langzamer aanvoelen, vooral tijdens het leren. Teams winnen vaak tijd terug door minder bugs en veiligere wijzigingen, hoewel het effect per project verschilt.
Blijf leren
Hulp nodig met testen, WordPress of SEO?
We helpen teams betere software te leveren en hun sites te laten groeien. Vertel ons waar u mee bezig bent en wij stellen een duidelijke volgende stap voor.
Meer handleidingen over testen, AI en moderne ontwikkeling
Genoten van dit artikel? Kies een onderwerp en ga direct naar onze nieuwste tutorials.
AI-hallucinaties uitgelegd: waarom taalmodellen dingen verzinnen en hoe ze te verminderen
AI-hallucinaties uitgelegd: wat ze zijn, waarom taalmodellen ze produceren, veelvoorkomende typen, hoe je ze kunt verminderen met aarding en aanwijz…
Lees artikel →DevOpsOmgevingsvariabelen uitgelegd: .env-bestanden, geheimen en configuratie goed gedaan
Omgevingsvariabelen uitgelegd met Python-voorbeelden die we hebben uitgevoerd: ze lezen en instellen, waarom elke waarde een string is, .env-bestand…
Lees artikel →SoftwareontwikkelingAPI-snelheidsbeperking uitgelegd: algoritmen, headers en afhandeling van 429-fouten
API-snelheidsbeperking uitgelegd met een simulatie die we hebben uitgevoerd: token-bucket, vast venster, schuifvenster, 429 reacties, headers van sn…
Lees artikel →TestautomatiseringSpotten in Python: unittest.mock, patch en de fouten die tests doen liegen
Spotten in Python met unittest.mock, getest: Mock en return_value, side_effect, patchen waar een naam wordt gebruikt, spec en autospec, spotdatums e…
Lees artikel →SoftwaretestenCodedekking uitgelegd: lijn versus filiaaldekking en wat 100% werkelijk betekent
Codedekking uitgelegd met een echte dekking.py-run: dekking van lijnen, vertakkingen en condities, een rapport lezen, waarom 100% dekking nog steeds…
Lees artikel →Databases & SQLDELETE versus TRUNCATE versus DROP in SQL: verschillen, terugdraaien en wanneer elk te gebruiken
DELETE versus TRUNCATE versus DROP uitgelegd met geteste SQL: wat elk verwijdert, terugdraaien, triggers, identiteitsreset, snelheid en hoe PostgreS…
Lees artikel →Databases & SQLSQL-weergaven uitgelegd: creëer, gebruik en vermijd de valkuilen, met voorbeelden
SQL-weergaven uitgelegd met query's die we hebben uitgevoerd: een weergave maken, live resultaten, kolommen verbergen, prestaties, bijwerkbare en ge…
Lees artikel →SoftwareontwikkelingTypeScript-interface versus type: echte verschillen, getest met de compiler
TypeScript-interface versus type, gecontroleerd met de echte compiler: wat beide kunnen doen, wat slechts één kan doen, samenvoegen van declaraties,…
Lees artikel →SoftwareontwikkelingPython *args en **kwargs uitgelegd: flexibele functies met duidelijke voorbeelden
Python *args en **kwargs uitgelegd met code die we hebben uitgevoerd: in- en uitpakken, parametervolgorde, argumenten die alleen trefwoorden en posi…
Lees artikel →SoftwareontwikkelingPython Lambda-functies: syntaxis, echt gebruik en wanneer u ze niet moet gebruiken
Python lambda-functies uitgelegd met code die we hebben uitgevoerd: syntaxis, sorteren met sleutel, kaart en filter, sluitingen, de laat-bindende va…
Lees artikel →Kunstmatige intelligentieWat zijn AI-agenten? Hoe ze werken, met een eenvoudige agentloop die u kunt uitvoeren
Wat zijn AI-agenten? Een duidelijke gids voor de agentenlus, tools, geheugen en vangrails, met een kleine Python-lus die we hebben uitgevoerd, echte…
Lees artikel →DevOpsMonitoring versus waarneembaarheid: het echte verschil, met logboeken, statistieken en sporen
Monitoring versus waarneembaarheid uitgelegd: bekende en onbekende fouten, logboeken, statistieken en sporen, een walkthrough voor afrekenincidenten…
Lees artikel →DevOpsDocker-volumes versus bindingsmounts: bewaar containergegevens op de juiste manier
Docker-volumes versus bind-mounts uitgelegd: waarom containergegevens verdwijnen, benoemde volumes, bind-mounts, tmpfs, Compose-voorbeelden, back-up…
Lees artikel →TestautomatiseringTDD versus BDD: belangrijkste verschillen, één functie die op beide manieren is gebouwd en wanneer deze moet worden gebruikt
TDD versus BDD uitgelegd met één kortingsfunctie die in beide richtingen is gebouwd: rood-groen-refactor met code die we hebben uitgevoerd, gegeven-…
Lees artikel →SoftwareontwikkelingGit merge vs rebase: verschillen, voorbeelden en wanneer je wat gebruikt
Git merge vs rebase uitgelegd met echte logoutput, een vergelijkingstabel, de gouden regel van rebase en een veilige teamwerkwijze.
Lees artikel →SoftwareontwikkelingPython list comprehension: syntaxis, voorbeelden en wanneer je het vermijdt
Python list comprehension uitgelegd: syntaxis, filteren, if-else, geneste lussen, gemeten tijden en wanneer een gewone for-lus beter is.
Lees artikel →Databases & SQLSQL JOIN uitgelegd met voorbeelden (Inner, Left, Right, Full)
Begrijp SQL joins stap voor stap met voorbeeldtabellen en queries: INNER, LEFT, RIGHT, FULL, CROSS en SELF, plus veelgemaakte fouten en prestatietips.
Lees artikel →