Classic TDD Vs London TDD Vs BDD When To Use Each
1. Why this topic matters
Many teams argue about Classic TDD vs London TDD vs BDD as if only one approach is correct.
In reality:
- They solve different problems
- They optimize for different feedback loops
- Mature teams use all three, intentionally
This article explains what each style is, what problem it fits, and how to choose without confusion.
2. Classic TDD
What it is
Classic TDD (also called Detroit School) focuses on:
- Testing real behavior
- Using real collaborators where possible
- Avoiding mocks unless absolutely necessary
You write tests in the familiar cycle:
Red → Green → Refactor
Characteristics
- Few mocks
- More integration-like unit tests
- Tests verify outputs and state, not interactions
Example (.NET)
[Fact]public void Calculate_ReturnsSumOfAllOrderItems(){ var order = new Order(new[] { new OrderItem(10, 2), new OrderItem(5, 1) }); var calculator = new OrderTotalCalculator(); var total = calculator.Calculate(order); total.Should().Be(25);}
When Classic TDD is the right choice
- Pure business logic
- Domain models
- Algorithms and calculations
- Code with few or no dependencies
3. London TDD (Mockist TDD)
What it is
London TDD focuses on:
- Interactions between objects
- Heavy use of mocks and stubs
- Designing systems as collaborating roles
Instead of checking state, you verify who talks to whom.
Characteristics
- Mock most dependencies
- Tests describe orchestration
- Very fast feedback
- Strong influence on design
Example (.NET)
public class PaymentProcessor{ private readonly IBankGateway _bankGateway; public PaymentProcessor(IBankGateway bankGateway) { _bankGateway = bankGateway; } public async Task ProcessAsync(Payment payment) { await _bankGateway.ChargeAsync(payment.Amount); }}[Fact]public async Task Should_Charge_BankGateway_When_ProcessingPayment(){ var bankGateway = new Mock<IBankGateway>(); var processor = new PaymentProcessor(bankGateway.Object); await processor.ProcessAsync(new Payment(100)); bankGateway.Verify(x => x.ChargeAsync(100), Times.Once);}
When London TDD is the right choice
- Application services
- Use cases
- Workflow orchestration
- Code with many dependencies
- Feature-flag-driven logic
4. BDD (Behavior-Driven Development)
What it is
BDD is not a replacement for TDD.
It is a way to:
- Describe behavior in business language
- Align developers, QA, and stakeholders
- Focus on what the system should do, not how
BDD often uses the Given / When / Then format.
Characteristics
- Scenario-focused
- Readable by non-developers
- Can be automated or not
- Works on top of Classic or London TDD
Example (.NET-style BDD test)
[Fact]public void Given_InsufficientBalance_When_Withdrawing_Then_TransactionIsRejected(){ var account = new BankAccount(balance: 50); var result = account.Withdraw(100); result.Should().Be(TransactionResult.Rejected);}
When BDD is the right choice
- Business rules
- Acceptance criteria
- Complex domain behavior
- Communication with non-technical stakeholders
5. Side-by-side comparison
| Aspect | Classic TDD | London TDD | BDD |
|---|
| Main focus | State & output | Interactions | Behavior & intent |
| Mocks | Rare | Frequent | Depends |
| Design influence | Low–Medium | High | Medium |
| Readability | Developer-focused | Developer-focused | Business-friendly |
| Typical layer | Domain | Application / Services | Domain & Acceptance |
6. When should I use each?
Ask yourself 3 questions
- Does this class have many dependencies?
- Is the logic mainly orchestration or calculation?
- Who needs to read and understand this test?
Decision guide
- Pure business logic? → Classic TDD
- Orchestrating many collaborators? → London TDD
- Business rules / acceptance criteria? → BDD
💡 BDD can sit on top of Classic or London TDD
7. Common mistakes
❌ Mocking everything
Leads to:
- Brittle tests
- Over-specified behavior
❌ Using London TDD for pure logic
Mocks add noise when calculations are simple.
❌ Thinking BDD is only about tools
BDD is a mindset, not Cucumber or SpecFlow.
8. A practical team guideline
Most successful teams:
- Use Classic TDD in the Domain layer
- Use London TDD in Application / Use Case layer
- Use BDD for naming, scenarios, and acceptance tests
Example Clean Architecture mapping:
- Domain => Classic TDD + BDD naming
- Application => London TDD
- Infrastructure => Integration tests
9. Final thoughts
Classic TDD, London TDD, and BDD are tools, not religions.
The real skill is knowing:
- What problem am I solving?
- Who am I optimizing feedback for?
Choose the style that makes your intent clearest and your system safer to change.