| name | beck-test-driven-development |
| description | Develop software in the style of Kent Beck, creator of Test-Driven Development and Extreme Programming. Emphasizes red-green-refactor, tests-first design, small steps, and emergent architecture. Use when writing new features, refactoring legacy code, or establishing development discipline. |
| tags | tdd, test-driven, red-green-refactor, unit-testing, design, refactoring, xp, agile, clean-code |
Kent Beck Test-Driven Development Style Guide
Overview
Kent Beck is the creator of Test-Driven Development (TDD) and Extreme Programming (XP), two of the most influential software development practices of the past 30 years. TDD inverts the traditional code-then-test approach: write a failing test first, make it pass with the simplest code, then refactor. This discipline produces clean, tested, well-designed code as a natural byproduct of the development process.
Core Philosophy
"Test-Driven Development is a way of managing fear during programming."
"Make it work, make it right, make it fast—in that order."
"I'm not a great programmer; I'm just a good programmer with great habits."
TDD is not primarily about testing—it's about design. Writing tests first forces you to think about interfaces before implementations, dependencies before details, and behavior before structure. The tests are a beneficial side effect of a disciplined design process.
Design Principles
-
Red-Green-Refactor: Write failing test → make it pass → improve the code.
-
Baby Steps: Take the smallest step that could possibly work.
-
YAGNI: You Aren't Gonna Need It—don't build what you don't need yet.
-
Simple Design: Code that passes tests, reveals intent, has no duplication, and has fewest elements.
-
Courage: Tests give you the courage to refactor aggressively.
The TDD Cycle
┌─────────────────────────────────────────┐
│ │
│ 1. RED: Write a failing test │
│ - Test doesn't compile? That's │
│ failing. Write minimal code │
│ to make it compile (still fail) │
│ │
│ │ │
│ ▼ │
│ │
│ 2. GREEN: Make it pass │
│ - Write the simplest code that │
│ could possibly work │
│ - Sins allowed: duplication, │
│ hardcoding, ugly code │
│ │
│ │ │
│ ▼ │
│ │
│ 3. REFACTOR: Clean up │
│ - Remove duplication │
│ - Improve names │
│ - Extract methods/classes │
│ - Tests must stay green │
│ │
│ │ │
│ ▼ │
│ (repeat) │
└─────────────────────────────────────────┘
When Practicing TDD
Always
- Write the test before the code
- Run the test and watch it fail (red)
- Write only enough code to pass the test
- Refactor only when tests are green
- Commit after each green-refactor cycle
- Keep tests fast (milliseconds, not seconds)
- Test behavior, not implementation
Never
- Write code without a failing test
- Write more than one failing test at a time
- Refactor while tests are red
- Skip the refactoring step
- Test private methods directly
- Let tests become slow
- Mock what you don't own
Prefer
- Small, focused tests over large integration tests
- One assertion per test (logical assertion)
- Descriptive test names that document behavior
- Testing through public interfaces
- Fake it till you make it
- Triangulation to drive generalization
- Obvious implementation when it's obvious
Code Patterns
The TDD Rhythm
def test_multiplication():
five = Dollar(5)
assert five.times(2) == Dollar(10)
class Dollar:
def __init__(self, amount):
self.amount = amount
def times(self, multiplier):
return Dollar(self.amount * multiplier)
def __eq__(self, other):
return self.amount == other.amount
def test_multiplication_by_three():
five = Dollar(5)
assert five.times(3) == Dollar(15)
():
five = Franc()
five.times() == Franc()
:
():
.amount = amount
():
Franc(.amount * multiplier)
():
.amount == other.amount
:
():
.amount = amount
():
(.amount == other.amount
() == (other))
():
():
Dollar(.amount * multiplier)
():
():
Franc(.amount * multiplier)
Fake It Till You Make It
def test_sum():
assert sum([1, 2, 3]) == 6
def sum(numbers):
return 6
def test_sum_different_numbers():
assert sum([2, 3, 4]) == 9
def sum(numbers):
result = 0
for n in numbers:
result += n
return result
def sum(numbers):
return builtins.sum(numbers)
Triangulation
def test_equality_same_amount():
assert Dollar(5) == Dollar(5)
def __eq__(self, other):
return True
def test_equality_different_amount():
assert Dollar(5) != Dollar(6)
def __eq__(self, other):
return self.amount == other.amount
def test_equality_different_types():
assert Dollar(5) != Franc(5)
def __eq__(self, other):
return (self.amount == other.amount and
type(self) == type(other))
Test Structure: Arrange-Act-Assert
def test_withdraw_reduces_balance():
account = Account(balance=100)
account.withdraw(30)
assert account.balance == 70
def test_given_account_with_balance_when_withdraw_then_balance_reduced():
account = Account(balance=100)
account.withdraw(30)
assert account.balance == 70
Test Doubles
class FakeRepository:
def __init__(self):
self.data = {}
def save(self, entity):
self.data[entity.id] = entity
def find(self, id):
return self.data.get(id)
class StubPriceService:
def get_price(self, symbol):
return 100.00
class SpyEmailService:
def __init__(self):
self.sent_emails = []
def send(self, to, subject, body):
self.sent_emails.append({
'to': to,
'subject': subject,
'body': body
})
():
repository = FakeRepository()
service = UserService(repository)
service.register(, )
saved_user = repository.find_by_email()
saved_user
saved_user.email ==
The Testing Pyramid
"""
/\
/ \
/ E2E \ <- Few: Slow, brittle, but necessary
/──────\
/ \
/Integration\ <- Some: Test component boundaries
/──────────────\
/ \
/ Unit Tests \ <- Many: Fast, focused, isolated
/────────────────────\
"""
def test_calculate_discount_for_premium_customer():
customer = Customer(tier='premium')
order = Order(total=100)
discount = calculate_discount(customer, order)
assert discount == 20
def test_order_service_persists_to_database():
db = create_test_database()
service = OrderService(db)
order = service.create_order(customer_id=1, items=[...])
persisted = db.find_order(order.id)
assert persisted == order
def test_checkout_flow():
browser = Browser()
browser.visit('/cart')
browser.click('Checkout')
browser.fill('card_number', '4242424242424242')
browser.click('Pay')
assert browser.text_present('Order confirmed')
Refactoring Patterns
def print_invoice(invoice):
print(f"Invoice #{invoice.id}")
total = 0
for item in invoice.items:
total += item.price * item.quantity
print(f"Total: ${total}")
def print_invoice(invoice):
print(f"Invoice #{invoice.id}")
print(f"Total: ${calculate_total(invoice)}")
def calculate_total(invoice):
return sum(item.price * item.quantity for item in invoice.items)
class Order:
def calculate_total(self): ...
def calculate_tax(self): ...
def calculate_shipping(self): ...
def format_for_email(self): ...
def (): ...
:
(): ...
(): ...
(): ...
:
(): ...
(): ...
(): ...
():
employee. == :
employee.hours * employee.rate
employee. == :
employee.salary /
employee. == :
employee.base + employee.sales * employee.commission_rate
:
():
.hours * .rate
:
():
.salary /
:
():
.base + .sales * .commission_rate
The TDD State Machine
class TDDPractitioner:
"""
The mental states of a TDD practitioner.
"""
def __init__(self):
self.state = 'THINKING'
self.tests_passing = True
def write_test(self):
"""Write a new failing test."""
assert self.state == 'THINKING'
assert self.tests_passing, "Fix failing tests before writing new ones"
self.tests_passing = False
self.state = 'RED'
def make_it_pass(self):
"""Write simplest code to pass the test."""
assert self.state == 'RED'
self.tests_passing = True
self.state = 'GREEN'
def refactor(self):
"""Improve the code while keeping tests green."""
assert self.state == 'GREEN'
assert .tests_passing
.tests_passing,
.state =
():
.state (, )
.tests_passing
Mental Model
Beck approaches development by asking:
- What's the next test? Start with what you want to prove
- What's the simplest way to pass? Don't over-engineer
- What duplication can I remove? Refactor mercilessly
- Am I scared? Write more tests until you're not
- Is this design emerging? Trust the process
The TDD Checklist
□ Write a test that expresses what you want
□ Run it and watch it fail (RED)
□ Write the simplest code to pass
□ Run tests and see them pass (GREEN)
□ Look for duplication to remove
□ Refactor while tests stay green
□ Commit the cycle
□ Repeat
Signature Beck Moves
- Red-Green-Refactor cycle
- Fake it till you make it
- Triangulation to generalize
- Obvious implementation when obvious
- Baby steps when uncertain
- Test list to track progress
- One assertion per test (conceptually)
- Test behavior, not implementation