All insights
Learning

TDD vs BDD: Why Comparing Them Is Asking the Wrong Question

TDD and BDD get pitched as rivals, but they solve different problems at different levels. Here is how they actually fit together.

Oct 1, 20267 min read
TDD vs BDD: Why Comparing Them Is Asking the Wrong Question — Tesbo

A developer picks up a payments ticket on a Monday morning, opens a search tab, and types "TDD vs BDD" because the team's wiki mentions both terms and nobody has ever explained which one they are actually supposed to be doing. Thirty minutes later they have read four blog posts, each picking a winner, and they are no closer to writing code. This happens on more teams than admit it. A ticket that should take an hour of focused work stretches into an afternoon of research that answers a question nobody needed answered.

The honest fix is to say plainly that the question rests on a mistake. TDD and BDD are not two ways of doing the same job. One is a way of writing code. The other is a way of agreeing what the code should do before anyone writes it. A team can run both, run either one alone, or run neither and still ship reliable software, as long as they understand what each one is actually for.

What TDD actually is

Test driven development is a coding discipline. A developer writes a small failing test, then writes the minimum code to make it pass. Then they clean up the code while the test stays green. The cycle repeats in minutes, sometimes dozens of times an hour, entirely inside the codebase.

  • Who does it: the engineer writing the implementation, alone at their keyboard
  • When: during active development, before and while the production code is written
  • What it produces: unit tests that live in the source repository next to the code
  • What it is for: shaping a design one small decision at a time and catching regressions immediately

Nobody outside engineering ever reads these tests. That is fine. They are a private tool for the person building the feature.

What BDD actually is

Behavior driven development is a conversation discipline. Before code gets written, someone from the business side, someone from QA, and someone from engineering sit down together. They describe the expected behavior in plain, structured sentences. They often use a Given, When, Then format. The point is to catch a misunderstanding about what "done" means while it is still a five minute conversation, not a rejected pull request three days later.

  • Who does it: a mixed group, typically product, QA, and engineering together
  • When: before development starts, during backlog refinement or a three amigos session
  • What it produces: readable scenarios that describe behavior, sometimes automated, sometimes not
  • What it is for: making sure everyone agreed on the requirement before anyone builds it

TDD and BDD, side by side

Seen next to each other, the two practices barely overlap in who runs them and when.

  • Who does it: TDD is run by the developer alone. BDD is run by product, QA, and engineering together
  • When it happens: TDD happens while code is being written. BDD happens before any code is written
  • What it produces: TDD produces unit tests that live in the repository. BDD produces readable behavior scenarios
  • What it is for: TDD shapes a design and catches regressions. BDD makes sure everyone agreed on the requirement first

The correction the search query needs

TDD operates inside the code. BDD operates at the boundary between people. They are not competing answers to the same question. There is no winner to declare.

A team doing strict TDD can still hold no requirement conversations at all. A team doing careful BDD scenario writing can still skip writing any unit tests. Neither combination is unusual. Neither one is automatically wrong. Most teams get more value from picking up pieces of both than from treating either as a religion.

Where BDD scenarios and documented test cases overlap, and where they don't

This is where the confusion usually starts. A BDD scenario and a documented test case look similar on the page. Both describe a starting condition, an action, and an expected result. In practice they diverge quickly.

A BDD scenario is written to be automated against the application, in a format a tool like Cucumber or SpecFlow can parse. A documented test case is written for a human tester to read and execute step by step. It carries no assumption that a machine will ever run it. Teams that try to make one artefact do both jobs usually end up with something that serves neither audience well.

Here is the warning worth stating plainly. Gherkin written by developers, for developers, phrased in terms of API calls and internal object names, is not a shared language with anyone else on the team. The whole point of Given, When, Then was to let a product manager or a QA lead read the scenario and confirm it matches their expectation. A scenario like "Given the user object has status flag 3" has quietly stopped being that.

If the product manager cannot read a scenario without asking an engineer to translate it, the BDD half of the exercise has failed. That is true even if the automation runs green every time.

The payments example, both ways

Say the requirement is: a checkout should reject a card payment over 5000 dollars without a second authorization step. Here is that requirement as a TDD unit test, the kind a developer writes alone while building the validation function.

```
test("rejects payment over 5000 without second auth"):
payment = build_payment(amount=5001, second_auth=false)
result = validate_payment(payment)
assert result.approved == false
assert result.reason == "SECOND_AUTH_REQUIRED"
```

And here is the same requirement as a BDD scenario. This is the kind a product manager, a QA lead, and an engineer write together in a refinement meeting.

```
Scenario: Large payment blocked without second auth
Given a checkout of 5001 dollars
And no second authorization done
When the payment is submitted
Then the payment is declined
And the customer sees a second auth prompt
```

Notice what each one is optimized for. The unit test is fast, narrow, and speaks the language of the codebase. The scenario is slower to write and speaks the language of the business rule instead.

A team that only had the unit test could still ship a checkout flow where the decline message says something confusing. Nothing forced anyone to agree on the customer facing wording. A team that only had the scenario could still ship a validation function riddled with edge case bugs. Nothing forced anyone to test the boundary conditions in code.

Where this connects to the rest of the testing conversation

The requirement conversation that produces a good BDD scenario is the same conversation that should produce good acceptance criteria on the ticket itself. That holds whether or not the team ever automates a single Gherkin line.

The instinct behind both practices is catching a problem before it reaches a customer rather than after. That is the same instinct behind shift left testing more broadly. None of these are separate initiatives competing for a team's attention. They are the same discipline applied at different points in the timeline.

A practical starting point for a mixed team

Most teams do not need a policy decision about TDD versus BDD. They need a working agreement about which conversations happen when. A reasonable starting point:

  • Hold a short requirement conversation before any nontrivial ticket starts, in plain language, whether or not it gets formalized as a Gherkin scenario
  • Let individual developers use TDD, partial TDD, or test after, based on what the code actually needs
  • Write documented test cases for anything a human tester will need to check, separate from whatever unit tests the developer wrote
  • Revisit only if regressions keep slipping through, and diagnose which layer actually failed before adding process

Questions people ask

Do I have to choose one over the other?

No. They operate at different levels, one inside the code and one at the requirement stage. Most mature teams use pieces of both without treating either as mandatory everywhere.

Is a BDD scenario the same thing as a test case?

Not quite. A BDD scenario is usually written to be automated and read by the whole team. A documented test case is written for a human tester to execute step by step, and the two often diverge once teams write them at scale.

What is the actual risk with Gherkin?

The risk is writing it in developer language, full of internal object names and API details. That quietly defeats the purpose of having a shared, readable scenario in the first place.

Can a small team skip BDD entirely?

Yes. A five minute plain language conversation about expected behavior captures most of the value even without the Given, When, Then format or any automation behind it.

Where does TDD fit if the team already writes detailed acceptance criteria?

TDD still operates one level down, inside the implementation. It answers a different question: how the developer builds and verifies the code, not what the business agreed the code should do.

Keep going

Try Tesbo, or get the next useful idea

Start building your testing workflow now, or get one practical email a month.

Start free

One email a month

What we shipped, what we learned, and the occasional infographic worth pinning. Unsubscribe in one click.