Test-Driven Development
Philosophy
Tests verify behavior through public interfaces, not implementation details. Good tests read like a spec and survive refactors; bad tests mock internals and break without a behavior change. See tests.md and mocking.md.
Anti-Pattern: Horizontal Slices
DO NOT write all tests first, then all implementation ("horizontal slicing") -- it produces tests that check imagined shape, not real behavior. Correct approach: vertical slices via tracer bullets -- one test, one implementation, repeat.
WRONG (horizontal):
RED: test1, test2, test3, test4, test5
GREEN: impl1, impl2, impl3, impl4, impl5
RIGHT (vertical):
RED→GREEN: test1→impl1
RED→GREEN: test2→impl2
RED→GREEN: test3→impl3
...
Workflow
1. Planning
- Confirm interface changes and which behaviors to test (prioritize) with the user
- Identify deep modules (small interface, deep implementation)
- Design for testability
- List behaviors to test, not implementation steps; get user approval
- Focus on critical paths and complex logic -- you can't test everything
2. RED-GREEN Loop
Start with a tracer bullet -- one test proving the path end-to-end -- then repeat per behavior:
RED: Write test → fails
GREEN: Minimal code to pass → passes
One test at a time; only enough code to pass it; don't anticipate future tests.
3. Refactor
After all tests pass, apply refactor candidates: extract duplication, deepen modules, apply SOLID where natural, run tests after each step. Never refactor while RED.
Checklist Per Cycle
[ ] Test describes behavior, not implementation
[ ] Test uses public interface only
[ ] Test would survive internal refactor
[ ] Code is minimal for this test
[ ] No speculative features added