# Test Driven Development

> Use when implementing any feature or bugfix, before writing implementation code

- Skill: `mrlyk/test-driven-development` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add mrlyk/test-driven-development`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mrlyk/test-driven-development/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: mrlyk (https://skillmd.com/u/mrlyk)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mrlyk/test-driven-development

---


# Test-Driven Development

TDD is the red → green loop. This skill is the reference that makes that loop produce tests worth keeping: what a good test is, where tests go, the anti-patterns, and the rules of the loop. Every section applies on every cycle — consult them before and during the loop, not after.

When exploring the codebase, read `CONTEXT.md` (if it exists) so test names and interface vocabulary match the project's domain language, and respect ADRs in the area you're touching.

## Resolve the Test Command

Before RED/GREEN verification, resolve the project's test command in this order:

1. Read `.superharness/spec/testing/index.md` when it exists.
2. Otherwise inspect the repository manifest and lockfile: `package.json`, Python manifests, `pom.xml`, Gradle files, or `Package.swift`.
3. Prefer the framework's single-file or single-test invocation for the red-green loop.
4. Record a newly discovered command in `.superharness/spec/testing/index.md` when that project convention allows updating the specification.

## What a good test is

Tests verify behavior through public interfaces, not implementation details. Code can change entirely; tests shouldn't. A good test reads like a specification — "user can checkout with valid cart" tells you exactly what capability exists — and survives refactors because it doesn't care about internal structure.

See [tests.md](tests.md) for examples and [mocking.md](mocking.md) for mocking guidelines.

## Seams — where tests go

A **seam** is the public boundary you test at: the interface where you observe behavior without reaching inside. Tests live at seams, never against internals.

**Test only at pre-agreed seams.** No test is written at an unconfirmed seam. You can't test everything — agreeing the seams up front is how testing effort lands on the critical paths and complex logic instead of every edge case.

For Full work, the approved plan task's **Public Seams** are already confirmed. Do not prompt the user again.

For Lite work, read `contract.md` **Test Seams** first; ask the user only when the relevant seam is missing or unclear.

## Verification mode — which slices get a test

Behavior slices carry a **verification mode** decided at plan time. Only `tested` slices enter the red-green loop. A `verified` slice is covered by its declared gate — run or confirm that gate's evidence; do not write a dedicated test for it. Never downgrade a `tested` slice on your own: when a slice looks misclassified, stop and return `NEEDS_CONTEXT` naming the slice instead of silently deviating.

When no mode is declared (legacy plans, direct invocations), treat a slice as `tested` only when it has an independent expectation source, meaningful error space (branching, boundaries, transformation, state transitions, protocol compatibility), and a failure observable at a public seam; otherwise name the gate that covers it.

## Anti-patterns

- **Implementation-coupled** — mocks internal collaborators, tests private methods, or verifies through a side channel (querying the database instead of using the interface). The tell: the test breaks when you refactor but behavior hasn't changed.
- **Tautological** — the assertion recomputes the expected value the way the code does (`expect(add(a, b)).toBe(a + b)`, a snapshot derived by hand the same way, a constant asserted equal to itself), so it passes by construction and can never disagree with the code. Expected values must come from an independent source of truth — a known-good literal, a worked example, the spec.
- **Horizontal slicing** — writing all tests first, then all implementation. Bulk tests verify *imagined* behavior: you test the *shape* of things rather than user-facing behavior, the tests go insensitive to real changes, and you commit to test structure before understanding the implementation. Work in **vertical slices** instead — one test → one implementation → repeat, each test a **tracer bullet** that responds to what the last cycle taught you.
- **Low-signal** — asserts static copy, fixed strings, constants, or framework-guaranteed behavior. The test can fail, but only when someone edits that value on purpose: requirement change and test change are always the same edit, so it catches zero unintended regressions. Distinct from tautological, which cannot fail by construction. Copy that is itself a contract is worth testing: legal, price, or payment wording, accessibility names, i18n key mappings, state-dependent copy, and prompt or skill text whose content is executable behavior.

## Rules of the loop

- **Red before green.** Write the failing test first, then only enough code to pass it. Don't anticipate future tests or add speculative features.
- **One slice at a time.** One seam, one test, one minimal implementation per cycle.
- **Refactoring is not part of the loop.** It belongs to the review stage after the red → green implementation cycle.

## Verification checklist

- [ ] The slice's verification mode is `tested`; each `verified` slice got its gate evidence confirmed, not a new test.
- [ ] The test observes an agreed public seam.
- [ ] Its expected value is independent of the implementation.
- [ ] The focused test failed for the intended missing behavior before implementation.
- [ ] One minimal implementation made that slice pass.
- [ ] Mocks, when needed, isolate only system boundaries.
- [ ] Focused verification is green before the next slice.

