|
| 1 | +# /aam-tdd - Test-Driven Development |
| 2 | + |
| 3 | +Guided TDD workflow for implementing features through red-green-refactor cycles. This is the full methodology behind `code-quality.md`'s one-liner: "Write a failing test first. Implement the minimal solution. Refactor after green." |
| 4 | + |
| 5 | +**Core principle:** Tests verify behavior through public interfaces, not implementation details. A good test survives internal refactors because it doesn't care about internal structure. |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## Step 0: Read Context |
| 10 | + |
| 11 | +Read `docs/strategy-roadmap.md` and find the **Quality Tier** section. |
| 12 | + |
| 13 | +- **Standard tier and above:** TDD is the expected workflow. Proceed. |
| 14 | +- **Lightweight tier:** TDD is optional. Ask: "Quality tier is Lightweight — TDD is optional. Proceed with TDD anyway? (y/n)" |
| 15 | + |
| 16 | +Also read `.claude/rules/code-quality.md` if it exists — the skill complements that rule, not replaces it. |
| 17 | + |
| 18 | +--- |
| 19 | + |
| 20 | +## Step 1: Planning |
| 21 | + |
| 22 | +Before writing any code: |
| 23 | + |
| 24 | +1. **Read existing tests** in the project to learn the test framework, naming conventions, file organization, and assertion style. Match the project's patterns. |
| 25 | +2. **Identify the public interface** — what functions, endpoints, or APIs will callers use? Aim for a small interface with deep implementation (fewer methods, simpler parameters, complexity hidden inside). |
| 26 | +3. **List 3-7 behaviors to test** — describe what the system does, not how. Each behavior should be observable through the public interface. |
| 27 | + - Good: "returns 404 when user not found" |
| 28 | + - Bad: "calls findById with the user ID" |
| 29 | +4. **Design for testability** — accept dependencies as parameters instead of creating them internally. Return results instead of producing side effects. Keep the surface area small. |
| 30 | +5. **Present the test plan** to the user for approval. |
| 31 | + |
| 32 | +You cannot test everything. Focus testing effort on critical paths and complex logic, not every edge case. Confirm priorities with the user. |
| 33 | + |
| 34 | +--- |
| 35 | + |
| 36 | +## Step 2: Tracer Bullet |
| 37 | + |
| 38 | +Write ONE test that confirms ONE thing about the system — the simplest vertical slice of the feature. |
| 39 | + |
| 40 | +``` |
| 41 | +RED: Write the test → it fails (confirms test infrastructure works) |
| 42 | +GREEN: Write minimal code to pass → it passes |
| 43 | +``` |
| 44 | + |
| 45 | +This is the tracer bullet — it proves the end-to-end path works and establishes the pattern for subsequent cycles. |
| 46 | + |
| 47 | +**Vertical slices, not horizontal layers.** Your first test should touch the real entry point and produce a real result, even if simplified. Do not write all tests first then all implementation — that produces tests of imagined behavior. |
| 48 | + |
| 49 | +--- |
| 50 | + |
| 51 | +## Step 3: Incremental Loop |
| 52 | + |
| 53 | +For each remaining behavior from the test plan: |
| 54 | + |
| 55 | +``` |
| 56 | +RED: Write the next test → it fails |
| 57 | +GREEN: Write minimal code to pass → it passes |
| 58 | +``` |
| 59 | + |
| 60 | +Rules: |
| 61 | +- One test at a time |
| 62 | +- Only enough code to pass the current test |
| 63 | +- Do not anticipate future tests |
| 64 | +- Keep tests focused on observable behavior |
| 65 | + |
| 66 | +**Mocking guidance:** Mock at system boundaries only — network calls, filesystem, clock, randomness. Do not mock your own modules or internal collaborators. Use dependency injection to swap external implementations in tests. Prefer specific mock functions per operation over a single generic mock. |
| 67 | + |
| 68 | +**Test quality check per cycle:** |
| 69 | +- [ ] Test describes behavior, not implementation |
| 70 | +- [ ] Test uses the public interface only |
| 71 | +- [ ] Test would survive an internal refactor |
| 72 | +- [ ] Code is minimal for this test |
| 73 | +- [ ] No speculative features added |
| 74 | + |
| 75 | +--- |
| 76 | + |
| 77 | +## Step 4: Refactor |
| 78 | + |
| 79 | +All tests are green. Now look for refactor candidates: |
| 80 | + |
| 81 | +- **Duplication** — extract shared logic into functions |
| 82 | +- **Long methods** — break into private helpers (keep tests on the public interface) |
| 83 | +- **Shallow modules** — if a module's interface is as complex as its implementation, combine or deepen it |
| 84 | +- **Feature envy** — logic that uses another module's data more than its own belongs in that other module |
| 85 | +- **Primitive obsession** — raw strings or numbers where a domain type would add clarity |
| 86 | +- **Existing code** the new code reveals as problematic |
| 87 | + |
| 88 | +Run tests after each refactor step. Never refactor while RED — get to GREEN first. |
| 89 | + |
| 90 | +--- |
| 91 | + |
| 92 | +## When to Use This |
| 93 | + |
| 94 | +- **Use `/aam-tdd`** when starting a new feature or when the test plan is non-obvious. |
| 95 | +- **Use `code-quality.md`** (loaded automatically at Standard+ tiers) for day-to-day TDD discipline without the full structured workflow. |
| 96 | +- Pairs well with `/aam-triage` — triage produces a fix plan as RED-GREEN cycles that this skill can execute. |
| 97 | + |
| 98 | +--- |
| 99 | + |
| 100 | +*Adapted from [mattpocock/skills/tdd](https://github.com/mattpocock/skills) (MIT license).* |
0 commit comments