Writing & Running Tests
Testing is not just something you add at the end — it is a workflow. Claude Code can write tests before the code exists, run your entire suite, diagnose failures, and fix the code or the test.
Two modes: generate vs TDD
You can use Claude Code for testing in two distinct ways. Use both — they serve different moments in the build cycle.
| Mode | When to use | Example prompt |
|---|---|---|
| Generate tests for existing code | You have a function or module already written. Ask Claude to cover it with tests. | “Write tests for @src/lib/format.ts — happy path, null input, empty string.” |
| TDD — test first, code second | Describe what you want to build. Claude writes failing tests first, then implements. | “Write failing tests for a formatCurrency(amount, currency) function, then implement it.” |
The TDD loop
Test-driven development follows a tight five-step cycle. Claude Code speeds up every step.
- Write the test
- Run — it fails (this proves the test checks something real)
- Write the code
- Run — it passes
- Refactor and repeat
Example A — generate tests for existing code
You have a formatCurrency utility already written. Ask Claude to write full coverage.
Write Vitest tests for @src/lib/format.ts. Cover: happy path (positive number), zero, negative number, a non-USD currency, and the NaN error case. Put the tests in src/__tests__/format.test.ts.
I'll write 5 tests covering all those cases. Each test will import formatCurrency directly and use expect() assertions.
Example B — TDD from scratch
You want a slugify utility but have not written it yet. Use TDD: tests first, implementation second.
Write failing tests
Write failing Vitest tests for a slugify(text: string): string function that does not exist yet. It should: lowercase the text, replace spaces with hyphens, strip special characters, and trim leading/trailing hyphens. Save to src/__tests__/slugify.test.ts. Do NOT create the implementation yet.
I'll write the tests first — they will all fail since slugify doesn't exist. This confirms the tests are actually testing something.
4 failures — expected. The function does not exist yet.
Implement to pass
Now create src/lib/slugify.ts — implement slugify() so that all tests in @src/__tests__/slugify.test.ts pass. Do not change the tests.
I'll implement slugify in src/lib/slugify.ts to satisfy every test case without touching the test file.
Running the full suite
Claude Code can run your entire test suite and report results back. Ask it from inside the Claude session — or run tests yourself in the integrated terminal.
Run the full test suite and tell me which tests are failing.
Running npm run test now. I'll report the results and highlight any failures.
For continuous feedback while you work, run Vitest in watch mode in a separate terminal:
Fixing a failing test
A test breaks after a code change. Paste the failure into Claude and it will diagnose whether the code is wrong or the test needs updating.
Test failing after my last change to @src/lib/format.ts: AssertionError: expected '$0.00' to equal '0'. The test expects '$0.00' but the function now returns '0'. Which is wrong — the test or the code?
The code is wrong. Zero is a valid currency amount and should be formatted as '$0.00'. Your recent change removed the Intl.NumberFormat call for zero values. I'll fix format.ts — the test should not change.
Prompting patterns that work
| Goal | Prompt pattern |
|---|---|
| Cover an existing function | "Write Vitest tests for @src/lib/x.ts — cover happy path, [list edge cases]." |
| TDD — tests first | "Write failing tests for [describe function]. Do NOT implement it yet." |
| TDD — implement | "Implement [function] so all tests in @src/__tests__/x.test.ts pass. Do not change the tests." |
| Fix a failure | "Test failing: [paste error]. File: @src/lib/x.ts. Is the code or the test wrong? Fix accordingly." |
| Run the suite | "Run npm run test and report which tests are failing and why." |
| Add a missing case | "Add a test to @src/__tests__/x.test.ts for the case where [describe scenario]." |
Works with any test framework
Claude Code adapts to whatever framework your project already uses. Name it in the prompt or rely on CLAUDE.md to carry the convention.
| Framework | Typical stack |
|---|---|
| Vitest | TypeScript / Vite |
| Jest | JavaScript / Node |
| Pytest | Python |
| Go test | Go |
CLAUDE.md once: Tests: Vitest, colocated in src/__tests__/. Every session knows which framework and file location to use without being told.Quick reference
| Practice | One-line rule |
|---|---|
| Name test cases explicitly | happy path, zero, null, error — not just "full coverage" |
| @-mention the file under test | Claude reads the source before writing tests |
| Keep framework in CLAUDE.md | Right framework and path every session |
| TDD: test first, implement second | Confirm tests fail before implementing |
| Do not change tests to make them pass | Add "do not change the tests" to implement prompts |
| Paste the exact error when fixing | Full error text speeds up diagnosis |
| Run watch mode while building | npx vitest --watch gives instant feedback on every save |
Before you continue
- Generate tests for existing code, or use TDD — tests first, implementation second.
- Name test cases explicitly and always @-mention the source file.
- Tell Claude “do not change the tests” when implementing to pass them.
- Paste the full error message when a test fails after a code change.
- Next: best practices that make Claude Code more effective every day.
What's Next
Your test coverage is growing. The final Claude Code lesson distils the best practices — habits and patterns that make Claude Code more effective every day.