100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace

Test-Driven Development (TDD) Cheat Sheet

Test-Driven Development (TDD) Cheat Sheet

Covers the red-green-refactor loop, writing the smallest failing test first, test doubles, and common TDD anti-patterns to avoid.

2 PagesBeginnerFeb 2, 2026

The Red-Green-Refactor Loop

Write a failing test, make it pass with the simplest code, then clean up — repeat in small steps.

python
# 1. RED — write a failing test for behavior that doesn't exist yetdef test_calculates_total_with_tax():    cart = ShoppingCart()    cart.add_item(price=100, tax_rate=0.08)    assert cart.total() == 108# 2. GREEN — write the minimum code to passclass ShoppingCart:    def __init__(self):        self.items = []    def add_item(self, price, tax_rate):        self.items.append((price, tax_rate))    def total(self):        return sum(p * (1 + t) for p, t in self.items)# 3. REFACTOR — clean up implementation/tests with the safety net of a green test#    (extract a Money value object, rename variables, etc.) — behavior stays the same

Test Doubles: Stub, Mock, Fake, Spy

Isolate the unit under test from slow/external dependencies.

python
from unittest.mock import Mockdef test_sends_confirmation_email_on_order():    email_service = Mock()  # mock: verifies interaction    order_service = OrderService(email_service=email_service)    order_service.place_order(order_id=1, email="a@example.com")    email_service.send.assert_called_once_with(        to="a@example.com", template="order_confirmation"    )class FakePaymentGateway:  # fake: working lightweight implementation    def __init__(self):        self.charges = []    def charge(self, amount):        self.charges.append(amount)        return Truedef test_charges_payment_gateway():    gateway = FakePaymentGateway()    service = CheckoutService(gateway)    service.checkout(amount=50)    assert gateway.charges == [50]

Parametrized Tests for Edge Cases

TDD works best when you drive out edge cases one small test at a time.

python
import pytest@pytest.mark.parametrize("price,tax_rate,expected", [    (100, 0.0, 100),     # no tax    (100, 0.08, 108),    # standard case    (0, 0.08, 0),        # zero price    (-10, 0.08, ValueError),  # invalid: negative price should raise])def test_total_with_various_inputs(price, tax_rate, expected):    cart = ShoppingCart()    if expected is ValueError:        with pytest.raises(ValueError):            cart.add_item(price, tax_rate)    else:        cart.add_item(price, tax_rate)        assert cart.total() == expected

TDD Principles & Anti-Patterns

What good TDD looks like, and the traps that erode it.

  • Arrange-Act-Assert- structure every test in these three clear sections
  • One assertion concept per test- keeps failures diagnostic and tests independent
  • Test behavior, not implementation- anti-pattern: asserting on private internals couples tests to refactors
  • Slow test suite- anti-pattern: hitting real DB/network in unit tests kills the fast feedback loop
  • Fragile mocks- anti-pattern: over-mocking collaborators makes tests break on harmless refactors
  • Triangulation- generalize an implementation only once a second test forces you to
  • F.I.R.S.T.- Fast, Independent, Repeatable, Self-validating, Timely — the properties of a good test
Pro Tip

If you're stuck writing the 'right' implementation, write the most deliberately fake/hardcoded version that passes the test first (e.g. `return 108`) — it forces you to write the next failing test that breaks the fake, which naturally drives out the real logic.

Was this cheat sheet helpful?

Explore Topics

#TestDrivenDevelopmentTDD#TestDrivenDevelopmentTDDCheatSheet#Programming#Beginner#Red#Green#Refactor#Loop#Testing#CheatSheet#SkillVeris