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. This lesson covers the full loop.
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.
Generate tests for existing code
You have a function or module already written. Ask Claude to cover it with tests — happy path, edge cases, and error conditions.
“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 writes the implementation to make them pass.
“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 — writing the test, writing the code, and fixing failures.
Write the test
Run — it fails
Write the code
Run — it passes
Refactor & repeat
Example A — Generate Tests for Existing Code
You have a formatCurrency utility already written. Ask Claude to write full coverage.
The existing function
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 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.
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 to run tests from inside the Claude session — or do it 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
The quality of Claude's tests depends heavily on how you ask. These patterns consistently produce useful, readable tests.
| 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. Just name it in the prompt or rely on CLAUDE.md to carry the convention.
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.Testing Workflow Quick Reference
Name the test cases explicitly
happy path, zero, null, error — not just "full coverage"
Always @-mention the file under test
Claude reads the source before writing tests
Keep tests and source in sync in CLAUDE.md
so Claude always uses the right framework and path
In TDD: test first, implement second
confirm the tests fail before asking Claude to implement
Never let Claude weaken assertions to pass tests
add "do not change the tests" to the implement prompt
Paste the exact error when fixing failures
Claude diagnoses the right cause much faster with the full error
Run watch mode while building
npx vitest --watch gives instant feedback on every save
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.