Testing Patterns
Universal testing strategies and patterns applicable across languages.
The Test Pyramid
code
/\
/ \ E2E Tests (few, slow, expensive)
/ \ - Full system tests
/------\ - Real browser/API calls
/ \
/ Integ \ Integration Tests (some)
/ Tests \ - Service boundaries
/--------------\ - Database, APIs
/ \
/ Unit Tests \ Unit Tests (many, fast, cheap)
------------------ - Single function/class
- Mocked dependencies
Test Types
Unit Tests
code
Scope: Single function/method/class Speed: Milliseconds Dependencies: All mocked When: Every code change Coverage: 80%+ of codebase
Integration Tests
code
Scope: Multiple components together Speed: Seconds Dependencies: Real databases, mocked external APIs When: PR/merge, critical paths Coverage: Key integration points
End-to-End Tests
code
Scope: Full user journey Speed: Minutes Dependencies: Real system (or staging) When: Pre-deploy, nightly Coverage: Critical user flows only
Test Naming Convention
code
test_<unit>_<scenario>_<expected> Examples: - test_calculate_total_with_discount_returns_reduced_price - test_user_login_with_invalid_password_returns_401 - test_order_submit_when_out_of_stock_raises_error
Arrange-Act-Assert (AAA)
python
def test_calculate_discount():
# Arrange - Set up test data and dependencies
cart = Cart()
cart.add_item(Item(price=100))
discount = Discount(percent=10)
# Act - Execute the code under test
total = cart.calculate_total(discount)
# Assert - Verify the results
assert total == 90
Test Doubles
| Type | Purpose | Example |
|---|---|---|
| Stub | Returns canned data | stub.get_user.returns(fake_user) |
| Mock | Verifies interactions | mock.send_email.assert_called_once() |
| Spy | Records calls, uses real impl | spy.on(service, 'save') |
| Fake | Working simplified impl | FakeDatabase() instead of real DB |
| Dummy | Placeholder, never used | null object for required param |
Test Isolation Strategies
Database Isolation
code
Option 1: Transaction rollback (fast) - Start transaction before test - Rollback after test Option 2: Truncate tables (medium) - Clear all data between tests Option 3: Separate database (slow) - Each test gets fresh database
External Service Isolation
code
Option 1: Mock at boundary - Replace HTTP client with mock Option 2: Fake server - WireMock, MSW, VCR cassettes Option 3: Contract testing - Pact, consumer-driven contracts
What to Test
MUST Test
- •Business logic and calculations
- •Input validation and error handling
- •Security-sensitive code (auth, permissions)
- •Edge cases and boundary conditions
SHOULD Test
- •Integration points (DB, APIs)
- •State transitions
- •Configuration handling
AVOID Testing
- •Framework internals
- •Third-party library behavior
- •Simple getters/setters
- •Private implementation details
Test Quality Checklist
- • Tests are independent (no order dependency)
- • Tests are deterministic (no flaky tests)
- • Tests are fast (unit < 100ms, integration < 5s)
- • Tests have clear names describing behavior
- • Tests cover happy path AND error cases
- • Tests don't repeat production logic
- • Mocks are minimal (only external boundaries)
Additional Resources
- •
./references/tdd-workflow.md- Test-Driven Development cycle - •
./references/mocking-strategies.md- When and how to mock - •
./references/test-data-patterns.md- Fixtures, factories, builders - •
./references/ci-testing.md- Testing in CI/CD pipelines
Scripts
- •
./scripts/coverage-check.sh- Run coverage and fail if below threshold