Guides

Test-driven development with Claude Code

TDD suits AI coding well. A failing test is an exact, checkable spec, and Claude can iterate against it without you judging every attempt. It only works if you stop the two classic failure modes: tests that can't fail, and code that "passes" because the test was changed.

The loop

  1. Write the test first, describing the behavior you want.
  2. Run it and watch it fail, for the right reason: an assertion failure, not an import error.
  3. Commit the failing test, so any later edit to it shows up in the diff.
  4. Implement until it passes, without touching the test.
  5. Mutation check: break the implementation on purpose and confirm the test goes red.
  6. Refactor with the test green.

Prompts that work

Step 1: tests only.

We're doing TDD. Write failing tests for: <behavior>.
- Follow the conventions in the nearest existing test files.
- Cover the main case, edge cases (empty, boundary, invalid input), and error paths.
- Do NOT write any implementation code yet.
- Run the tests and show me that they fail, and why.

Step 2: implement.

The tests in <file> are the spec. Make them pass.
- Do not modify, skip, or weaken the tests. If you believe a test is wrong,
  stop and explain why instead of changing it.
- Run the full test file after each change.

Step 3: prove the tests bite.

Temporarily introduce 3 plausible bugs in the implementation, one at a time
(off-by-one, inverted condition, missing error check). Confirm at least one
test fails for each, then revert each bug. Report any bug that no test caught.

Pitfall 1: Claude edits the test to make it pass

Under pressure to get to green, an agent may loosen an assertion, add .skip, or change the expected value to match buggy output. Defend in layers:

Pitfall 2: tests that can't fail

Watch for tests that assert on a mock's return value, check only that "no exception was thrown", or compute the expected value with the same logic as the code under test. The mutation check in step 3 exposes these.

Pitfall 3: over-mocking

If every dependency is mocked, the test verifies your mocks, not your code. Mock at real boundaries: the network, the clock, payment providers. Use the real database through fixtures if the project already does. Tell Claude which boundaries your team mocks.

Pitfall 4: testing implementation details

Tests that assert on private methods or exact call counts break on every refactor. Ask for behavior-level assertions: inputs, outputs and observable side effects.

When TDD fits best

It fits less well for exploratory UI work, where a quick prototype and a screenshot review are usually faster.

Make it the default

Add a short section to your CLAUDE.md so every session follows the loop:

## Testing
- For bug fixes and new logic: write a failing test first and show it failing.
- Never modify, skip or weaken existing tests to make them pass; ask instead.
- Single test: `<your test command> path/to/test -k name`

Skip the setup: get the tested versions

Keelwork bundles 10 workflow skills, 5 tested safety hooks (including a full guard-bash and a secret scanner), 3 subagents and 5 CLAUDE.md templates, with a one-command installer that safely merges into your settings.

Get Keelwork — $24 →