Testing Patterns
Universal testing patterns that apply across languages and frameworks.
Test-Driven Development (TDD)
The TDD Cycle
code
Red → Green → Refactor
- •Red: Write a failing test
- •Green: Write minimal code to pass
- •Refactor: Improve code, keep tests passing
TDD Example (Pseudocode)
code
# Step 1: Red - Write failing test
test "add returns sum of two numbers":
assert add(2, 3) == 5 # Fails - function doesn't exist
# Step 2: Green - Minimal implementation
function add(a, b):
return 5 # Passes, but not generalized
# Step 3: Refactor - Proper implementation
function add(a, b):
return a + b # Correct implementation
Test Structure
Arrange-Act-Assert (AAA)
code
test "user can log in with valid credentials":
# Arrange - Set up test conditions
user = create_test_user(email="test@example.com", password="secret")
# Act - Perform the action
result = login(email="test@example.com", password="secret")
# Assert - Verify the outcome
assert result.success == true
assert result.user.email == "test@example.com"
Given-When-Then (BDD Style)
code
test "authenticated user can access dashboard":
# Given a logged-in user
user = login_as_test_user()
# When they access the dashboard
response = get_dashboard(user)
# Then they see their data
assert response.status == 200
assert response.contains(user.name)
Mocking Patterns
When to Mock
Mock when:
- •External services (APIs, databases)
- •Slow operations
- •Non-deterministic behavior (time, random)
- •Side effects (emails, payments)
Don't mock:
- •The thing you're testing
- •Simple value objects
- •When a real implementation is easy
Mock Types
Stub
Returns predetermined values:
code
stub_api.get_user = returns({ id: 1, name: "Test" })
Spy
Records calls for verification:
code
spy_logger = spy(logger)
do_something()
assert spy_logger.called_with("Something happened")
Fake
Working implementation with shortcuts:
code
fake_db = InMemoryDatabase() # Real behavior, no persistence
Test Categories
Unit Tests
- •Test one function/method in isolation
- •Fast (milliseconds)
- •Mock dependencies
Integration Tests
- •Test components working together
- •Use real dependencies where practical
- •Slower (seconds)
End-to-End Tests
- •Test full user flows
- •Real browser/client
- •Slowest (minutes)
Testing Pyramid
code
/\
/ \ E2E (few)
/────\
/ \ Integration (some)
/────────\
/ \ Unit (many)
/────────────\
Quick Checklist
Before completing a feature:
- • Happy path tested
- • Error cases tested
- • Edge cases tested
- • All tests pass
- • No flaky tests introduced