Test-Driven Development
Drive production code from a failing test, one small step at a time: red (write a test that fails), green (make it pass by any means), refactor (remove the duplication you just created). The goal is clean code that works, and the design emerges from the tests rather than being drawn up front.
Essentials
- Run the loop red -> green -> refactor - never skip refactor; green-without-refactor is not TDD, see references/red-green-refactor.md
- Write the failing test before the code - production code exists only to make a red test pass, see references/red-green-refactor.md
- Pick a green-bar strategy per step - obvious implementation, fake it, or triangulate, see references/green-bar-strategies.md
- Keep a test list, work one item at a time - list test ideas, never batch-write them, see references/test-list-and-design.md
- Let the tests grow the design - the emerging API is an output; judge the refactor against oop-guide and connascence-guide, see references/test-list-and-design.md
Gotchas
- Skipping refactor leaves the duplication you committed to go green (including a constant duplicated between test and code): removing it is exactly what the third step is for.
- "Refactor" means change structure without changing behaviour while the test stays green; it is not "rewrite", "optimize", or "add the next feature".
- The test list is a list of test IDEAS, not a batch of tests written up front: write them one at a time to preserve the cycle.
- Test-first is the rhythm; classical vs mockist is an orthogonal design choice. You can do classical TDD test-first, so don't conflate the two.
- Neither classical nor mockist is "more modern": mockist trades integration coverage and tighter implementation-coupling for faster, smaller fixtures.
- A single good test's anatomy (AAA, FIRST, naming, the test-double taxonomy) belongs to testing-guide. This skill owns only the rhythm in which you write it.
Example
Test list (shopping-cart total):
[ ] empty cart totals 0
[ ] one line item totals its price
[ ] two items sum
[ ] a percentage discount applies
RED test: total([]) === 0 -> fails (no function)
GREEN return 0 -> obvious implementation
RED test: total([{price: 500}]) === 500 -> fails
GREEN return items[0].price -> fake it (duplicated constant)
RED test: total([a, b]) === a.price + b.price -> fails, forces generalize
GREEN items.reduce((s, i) => s + i.price, 0) -> sum; constant duplication gone
REFACTOR name the fold, extract lineTotal() -> structure only, test stays green
Progressive Disclosure
- Read references/red-green-refactor.md - Load when running or coaching the cycle: the two rules, what each of red/green/refactor allows, small steps, and why refactor is non-optional.
- Read references/green-bar-strategies.md - Load when deciding how to make a red test pass: obvious implementation vs fake it vs triangulation and how to choose.
- Read references/test-list-and-design.md - Load when planning what to test next or letting tests shape the API: the test list, one-at-a-time discipline, classical vs mockist as a design choice.
1---2name: tdd-guide3description: Use when driving code test-first or coaching the red-green-refactor rhythm: writing a failing test before the production code, going green with fake-it / obvious-implementation / triangulation, then refactoring to remove duplication, working a test list one item at a time, and letting the tests grow the design. Triggers on red-green-refactor, test-first, 'write the test first', failing test before code, triangulation, fake it till you make it, test list / to-do list, classical (Detroit) vs mockist (London) TDD, and 'clean code that works', even when the user doesn't say 'TDD'.4---56# Test-Driven Development78Drive production code from a failing test, one small step at a time: red (write a test that fails), green (make it pass by any means), refactor (remove the duplication you just created). The goal is clean code that works, and the design emerges from the tests rather than being drawn up front.910## Essentials1112- **Run the loop red -> green -> refactor** - never skip refactor; green-without-refactor is not TDD, see [references/red-green-refactor.md](references/red-green-refactor.md)13- **Write the failing test before the code** - production code exists only to make a red test pass, see [references/red-green-refactor.md](references/red-green-refactor.md)14- **Pick a green-bar strategy per step** - obvious implementation, fake it, or triangulate, see [references/green-bar-strategies.md](references/green-bar-strategies.md)15- **Keep a test list, work one item at a time** - list test ideas, never batch-write them, see [references/test-list-and-design.md](references/test-list-and-design.md)16- **Let the tests grow the design** - the emerging API is an output; judge the refactor against **oop-guide** and **connascence-guide**, see [references/test-list-and-design.md](references/test-list-and-design.md)1718## Gotchas1920- Skipping refactor leaves the duplication you committed to go green (including a constant duplicated between test and code): removing it is exactly what the third step is for.21- "Refactor" means change structure without changing behaviour while the test stays green; it is not "rewrite", "optimize", or "add the next feature".22- The test list is a list of test IDEAS, not a batch of tests written up front: write them one at a time to preserve the cycle.23- Test-first is the rhythm; classical vs mockist is an orthogonal design choice. You can do classical TDD test-first, so don't conflate the two.24- Neither classical nor mockist is "more modern": mockist trades integration coverage and tighter implementation-coupling for faster, smaller fixtures.25- A single good test's anatomy (AAA, FIRST, naming, the test-double taxonomy) belongs to **testing-guide**. This skill owns only the rhythm in which you write it.2627## Example2829```text30Test list (shopping-cart total):31[ ] empty cart totals 032[ ] one line item totals its price33[ ] two items sum34[ ] a percentage discount applies3536RED test: total([]) === 0 -> fails (no function)37GREEN return 0 -> obvious implementation38RED test: total([{price: 500}]) === 500 -> fails39GREEN return items[0].price -> fake it (duplicated constant)40RED test: total([a, b]) === a.price + b.price -> fails, forces generalize41GREEN items.reduce((s, i) => s + i.price, 0) -> sum; constant duplication gone42REFACTOR name the fold, extract lineTotal() -> structure only, test stays green43```4445## Progressive Disclosure4647- Read [references/red-green-refactor.md](references/red-green-refactor.md) - Load when running or coaching the cycle: the two rules, what each of red/green/refactor allows, small steps, and why refactor is non-optional.48- Read [references/green-bar-strategies.md](references/green-bar-strategies.md) - Load when deciding how to make a red test pass: obvious implementation vs fake it vs triangulation and how to choose.49- Read [references/test-list-and-design.md](references/test-list-and-design.md) - Load when planning what to test next or letting tests shape the API: the test list, one-at-a-time discipline, classical vs mockist as a design choice.