# Tdd

> Test-driven development via the red-green-refactor loop using vertical slices (one test → one implementation at a time). Tests verify behavior through public interfaces, not implementation details. Use when user wants to build a feature or fix a bug test-first, says "TDD", "red-green-refactor", "test-first", "write tests first", asks for integration tests, or invokes `/tdd`.

- Skill: `orakitine/tdd` (Agent Skill, multi-file: 8 files)
- Install (CLI): `npx skillmds@latest add orakitine/tdd`
- Raw SKILL.md: https://api.skillmd.com/api/skills/orakitine/tdd/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: orakitine (https://skillmd.com/u/orakitine)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/orakitine/tdd

---


# Purpose

Drive feature development and bug fixes through tight red-green-refactor cycles, one behavior at a time. Tests verify observable behavior through public interfaces so they survive internal refactors. Vertical slices (tracer bullets) replace the "write all tests, then all code" anti-pattern because tests written in bulk inevitably test imagined behavior — not actual behavior.

## Philosophy

**Tests verify behavior through public interfaces, not implementation details.** Code can change entirely; tests shouldn't.

- **Good tests** are integration-style: they exercise real code paths through public APIs. They describe _what_ the system does, not _how_. A good test reads like a specification — "user can checkout with valid cart" tells you exactly what capability exists. These tests survive refactors because they don't care about internal structure.
- **Bad tests** are coupled to implementation. They mock internal collaborators, test private methods, or verify through external means (querying a database directly instead of using the interface). The warning sign: your test breaks when you refactor, but behavior hasn't changed. If you rename an internal function and tests fail, those tests were testing implementation, not behavior.

**Anti-pattern: horizontal slices.** DO NOT write all tests first, then all implementation. That treats RED as "write all tests" and GREEN as "write all code." It produces crap tests because:

- Tests written in bulk test _imagined_ behavior, not _actual_ behavior
- You end up testing the _shape_ of things (data structures, function signatures) rather than user-facing behavior
- Tests become insensitive to real changes — they pass when behavior breaks, fail when behavior is fine
- You outrun your headlights, committing to test structure before understanding the implementation

**Correct approach: vertical slices via tracer bullets.** One test → one implementation → repeat. Each test responds to what you learned from the previous cycle.

```
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. **Plan with the user before writing any code**
   - Explore the codebase. Use the project's domain glossary so test names and interface vocabulary match the project's language. Respect ADRs in the area you're touching.
   - Confirm with the user what interface changes are needed
   - Confirm with the user which behaviors to test, and prioritize them — you can't test everything, so focus on critical paths and complex logic, not every possible edge case
   - Identify opportunities for **deep modules** (small interface, deep implementation) — see `./references/deep-modules.md`
   - Design interfaces for **testability** (accept dependencies, return results, small surface) — see `./references/interface-design.md`
   - List behaviors to test (not implementation steps)
   - Get user approval on the plan before proceeding
   - Example: User asks to add a checkout flow → ask "What should the public `checkout()` interface look like? Which behaviors matter most — happy path, invalid cart, payment failure, partial refund?" Get explicit prioritization before any test is written.

2. **Fire the tracer bullet**
   - Write ONE test that confirms ONE thing about the system
   - RED: test fails (confirm it fails for the right reason — not a syntax error)
   - GREEN: write the minimum code to make it pass
   - This proves the path works end-to-end before you build more on top of it
   - Example:
     ```
     RED:   test("checkout with valid cart returns confirmed status") → fails (no checkout fn)
     GREEN: implement minimal checkout() returning { status: "confirmed" } → test passes
     ```

3. **Loop one behavior at a time**
   - For each remaining behavior, run RED → GREEN
   - Rules:
     - One test at a time
     - Only enough code to pass the current test
     - Do NOT anticipate future tests
     - Keep tests focused on observable behavior through the public interface
   - After each GREEN, run the **per-cycle checklist**:
     - [ ] Test describes behavior, not implementation
     - [ ] Test uses public interface only
     - [ ] Test would survive an internal refactor
     - [ ] Code is minimal for this test
     - [ ] No speculative features added
   - IF: the test breaks when you rename an internal function but behavior is unchanged → THEN the test is coupled to implementation; rewrite it against the public interface (see `./references/tests.md` for good/bad examples)
   - IF: the behavior crosses a system boundary (external API, database, time, randomness, filesystem) → THEN mock at that boundary only — never mock internal collaborators (see `./references/mocking.md`)
   - Example: Next behavior is "checkout with empty cart returns error" → write that test alone, watch it fail, add the minimal guard clause, watch it pass, then run the checklist.

4. **Refactor only while GREEN**
   - Never refactor while RED. Get to GREEN first.
   - After all tests for the current slice pass, look for refactor candidates: duplication, long methods, shallow modules, feature envy, primitive obsession, and existing code the new code reveals as problematic (see `./references/refactoring.md`)
   - Apply changes:
     - Extract duplication
     - Deepen modules (move complexity behind simple interfaces)
     - Apply SOLID principles where natural
     - Consider what the new code reveals about existing code
   - Run tests after EACH refactor step. If anything goes red, undo or fix immediately before the next step.
   - Example: After three checkout tests are green you notice cart-total math repeated in two places → extract `calculateTotal(cart)`, run tests, all green → continue.

5. **Return to the loop or finish**
   - IF: more prioritized behaviors remain → return to step 3
   - IF: all prioritized behaviors are covered and refactor pass is clean → summarize what was built, which behaviors are covered, and any deferred behaviors the user explicitly chose not to test
   - Example: "Checkout flow done. Covered: valid cart, empty cart, payment failure. Deferred per your call: partial refund (edge case, low priority)."

## References

### Good vs. bad tests, with examples

- IF: deciding whether a test is well-formed, debugging a test that breaks on refactor, or unsure what to assert
- THEN: Read `./references/tests.md`
- EXAMPLES:
  - "is this test testing implementation or behavior?"
  - "my tests keep breaking when I rename things"
  - "what should I assert here?"

### Mocking and system boundaries

- IF: about to mock something, designing an external integration, or wondering if a dependency should be injected
- THEN: Read `./references/mocking.md`
- EXAMPLES:
  - "should I mock the database?"
  - "how do I test this Stripe integration?"
  - "this function is hard to mock — what do I change?"

### Interface design for testability

- IF: designing a new function or module, or an existing one is painful to test
- THEN: Read `./references/interface-design.md`
- EXAMPLES:
  - "this function takes too many params to test"
  - "how should I shape this API?"
  - "the test setup is enormous — what's wrong with the interface?"

### Deep modules (small interface, deep implementation)

- IF: an interface feels too wide, too many methods, or wraps trivial logic
- THEN: Read `./references/deep-modules.md`
- EXAMPLES:
  - "is this module too shallow?"
  - "should I split or combine these classes?"
  - "this wrapper does almost nothing — keep it?"

### Refactor candidates after GREEN

- IF: all tests pass and you're entering the refactor step
- THEN: Read `./references/refactoring.md`
- EXAMPLES:
  - "what should I refactor now that tests pass?"
  - "I see duplication — extract or leave?"
  - "the new code makes the old code look bad — what do I do?"

## Works well with

Optional collaborators — `tdd` runs standalone and these degrade gracefully if absent.

- **`grill-with-docs`** (or `grill-me`) — align on scope, interface, and behaviors before the first test; `grill-with-docs` also records the ADRs `tdd` should respect.
- **`living-plan`** — its `build` workflow delegates the test-first implementation of each task to `tdd`, handing over the plan's behaviors as the approved design. `tdd` stays fully usable without it.
- **`diagnose`** — after a diagnosis pins a bug, lock the fix with a `tdd` regression test.
- **`setup-toolbox-context`** — provides the `CONTEXT.md` vocabulary `tdd` uses for test and interface names.

