BDD, ATDD and Specification by Example
“TDD or BDD?” is asked as if they competed. They are the same loop at different altitudes, and the interview answer is knowing what BDD adds, what it costs, and the one condition under which it pays for itself.
The same loop, one level up
TDD’s inner loop is write a failing test, make it pass, refactor. BDD keeps
the loop and changes the unit: instead of a test named
test_discount_applies_over_threshold, a scenario written in language the
business can read.
Feature: Order discounts
Scenario: Orders over the threshold get the bulk discount
Given a customer with an empty basket
When they add 12 units of "widget" at 10.00
Then the order total is 108.00
And the discount line reads "bulk 10%"Given is the state, When the action, Then the observable outcome. The format is Gherkin, and the feature file is meant to be the specification the product owner signs off — which is the entire point, and also the entire cost.
ATDD / Specification by Example is the acceptance-level version: the examples are agreed with stakeholders before work starts and become the definition of done. BDD is the practice; Specification by Example is the book that says why (Adzic).
In Python
# steps/orders.py - pytest-bdd binds each line to a function.
from pytest_bdd import given, when, then, scenarios, parsers
scenarios("../features/discounts.feature")
@given("a customer with an empty basket", target_fixture="basket")
def empty_basket():
return Basket()
@when(parsers.parse('they add {qty:d} units of "{sku}" at {price:f}'))
def add_units(basket, qty, sku, price):
basket.add(sku, qty, Decimal(str(price)))
@then(parsers.parse("the order total is {total:f}"))
def check_total(basket, total):
assert basket.total() == Decimal(str(total))pytest-bdd keeps you in pytest — fixtures, plugins, the same runner.
behave is the standalone alternative with its own runner and a closer
match to Cucumber’s conventions. Choose pytest-bdd unless the team already
runs Cucumber elsewhere and wants one dialect.
The step definitions are the tax. Every phrasing in a feature file needs a binding, so a wording change breaks a step, and a team that writes scenarios freely ends up with hundreds of near-duplicate step functions.
When it pays, and when it is theatre
BDD earns its cost under exactly one condition: someone who is not an engineer reads the scenarios. A product owner who reviews feature files, argues about an example and catches a misunderstanding before code exists — that is the return, and it is large.
Without that reader, Gherkin is an indirection layer over pytest: the same tests, harder to write, harder to refactor, with a parser in the middle. Most teams that adopt BDD end up here, because the business reader was assumed rather than confirmed. It is worth saying in an interview that you have seen this failure, because it is the common one.
| Signal | BDD is |
|---|---|
| Product reviews scenarios | earning its cost |
| Engineers write both sides | pytest with extra steps |
| Scenarios mirror unit tests | duplicated |
The examples matter more than the format. The lasting idea from Specification by Example is the conversation — three amigos (product, dev, test) working through concrete examples until the edge cases surface. That conversation is valuable whether or not the result is a Gherkin file; a table of examples in a ticket captures most of it.
Where it breaks in production
- Scenarios as unit tests. A feature file asserting on internal method behaviour is a unit test wearing a costume. Keep BDD at the behaviour boundary — an API, a workflow — and unit-test underneath with pytest.
- Step-definition sprawl. Free-form wording yields one binding per phrasing. Agree a small vocabulary and reuse it.
- Slow suites. Scenarios tend to drive the whole stack, so they sit at the top of the pyramid and need the same restraint as E2E tests — see Test Strategy and the Test Pyramid.
- Nobody reads the report. The living-documentation output is only documentation if someone opens it.
Related
Interview angle 4
- “TDD or BDD?” - the same loop at different altitudes. TDD is the developer’s inner loop; BDD adds a scenario a non-engineer can read, and it pays for itself only when that non-engineer actually reads it. Follow-up: what is ATDD? Acceptance examples agreed with stakeholders before the work starts - BDD’s definition-of-done cousin.
- “When is BDD a mistake?” - when engineers write both the feature file and the steps and nobody else looks. Then it is pytest with a parser in the middle, harder to refactor, and the step definitions sprawl.
- “
pytest-bddorbehave?” -pytest-bddto stay inside pytest with its fixtures and plugins;behavewhen the team already speaks Cucumber and wants one dialect across languages. - “What survives if you drop Gherkin?” - the conversation. Working through concrete examples with product and test until the edge cases appear is the value; a table of examples in the ticket keeps most of it.