Originally published at claudeguide.io/claude-code-test-generation
Claude Code Test Generation: Auto-Write Unit & Integration Tests
Yes — Claude Code can generate tests automatically. Point it at any existing function, class, or module and it will produce complete pytest or Jest test files covering happy paths, edge cases, and error conditions. You do not need to write a prompt from scratch: a single slash command is enough. Teams that use structured test generation prompts produce 2.7x more test coverage per sprint versus writing tests manually, while maintaining equivalent defect detection. For projects with 0% test coverage, Claude Code can bootstrap a full suite in 1-2 hours.
This guide covers every pattern: generating tests from existing code, TDD (write tests first), integration test scaffolding, and the write → run → fix loop that makes test generation genuinely useful rather than a one-shot party trick.
Generating Unit Tests from Existing Code
The most common use case is retrofitting tests onto code that has none. Claude Code handles this with a direct instruction or a custom slash command.
Direct instruction pattern
Open Claude Code in your project and run:
Write pytest unit tests for src/billing/invoice.py. Cover the calculate_total, apply_discount, and generate_pdf functions. Include edge cases for zero amounts, negative discounts, and missing required fields.
Claude Code reads the source file, identifies the public API, and produces a tests/test_invoice.py file. It will also check whether conftest.py exists and add fixtures if needed.
Slash command pattern (repeatable)
If you generate tests regularly, encode the instruction as a slash command in your project's .claude/commands/ directory:
<!-- .claude/commands/gen-tests.md --
---
## Integration Test Patterns
Integration tests are harder because they cross module boundaries and often need databases, HTTP clients, or message queues. Claude Code handles this well when you give it the full call chain.
### Database integration test (pytest + SQLAlchemy)
Write pytest integration tests for the UserRepository class in src/repository/user.py. Use an in-memory SQLite database via SQLAlchemy. Cover create, read, update, delete, and the find_by_email query.
Claude Code will:
1. Detect that `UserRepository` depends on a `Session` object
2. Create a `conftest.py` with an `in_memory_db` fixture using `create_engine("sqlite:///:memory:")`
3. Write tests that use the fixture and roll back after each test
4. Run `pytest tests/integration/` and fix any SQLAlchemy version incompatibilities
**Resulting conftest.py**:
python
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from src.models import Base
@pytest.fixture(scope="function")
def db_session():
engine = create_engine("sqlite:///:memory:", echo=False)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
yield session
session.rollback()
session.close()
Base.metadata.drop_all(engine)
### HTTP API integration test (Jest + supertest)
plaintext
Write supertest integration tests for the Express router in src/routes/users.ts. Cover POST /users (create), GET /users/:id, PUT /users/:id, and DELETE /users/:id. Mock the database layer with jest.mock.
Claude Code will scaffold tests using `supertest` and `jest.mock("../db")`, set up `beforeEach`/`afterEach` cleanup, and assert on status codes, response bodies, and error messages.
---
## Running Tests in a Loop: Write → Run → Fix
The most powerful pattern is not one-shot test generation — it is the iterative loop where Claude Code writes tests, runs them, reads the failure output, and fixes either the tests or the implementation until everything is green.
Trigger this explicitly:
plaintext
Generate tests for src/payments/stripe_client.py, run them, and keep iterating until all tests pass. Do not stop until pytest exits 0.
Claude Code will:
1. Write an initial test file
2. Run `pytest` and capture stdout/stderr
3. Parse failure messages (import errors, assertion mismatches, missing fixtures)
4. Apply targeted fixes
5. Re-run, repeat until clean
For long-running cycles, you can encode this loop as a [Claude Code hook](/claude-code-hooks) that fires `pytest` automatically after every `Write` tool call to a `tests/` file:
json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "if echo \"$CLAUDE_FILE_PATH\" | grep -q '^tests/'; then pytest \"$CLAUDE_FILE_PATH\" --tb=short 2
Frequently Asked Questions
Can Claude Code generate tests for code it did not write?
Yes. Claude Code reads any source file you point it at, regardless of who wrote it. It infers expected behavior from the implementation — function signatures, docstrings, type hints, and internal logic. For undocumented code with no type hints, it still generates useful tests, though it may add comments noting assumptions it made about intended behavior.
Does Claude Code update tests when the source code changes?
Not automatically — but you can make it do so. Add a PostToolUse hook (see the Claude Code Hooks guide) that detects writes to source files and triggers a prompt to check whether the corresponding test file needs updating. Alternatively, instruct Claude Code at the start of a session: "Before finishing any task that edits source files, check whether the corresponding tests in tests/ need to be updated and update them."
What test frameworks does Claude Code support?
Claude Code is framework-agnostic. It can write tests for pytest, unittest, nose2 (Python), Jest, Vitest, Mocha, Jasmine (JavaScript/TypeScript), RSpec (Ruby), JUnit (Java), Go's built-in testing package, Rust's #[test] attribute, and others. Specify the framework in your instruction or in CLAUDE.md and it will follow that convention. If you do not specify one, Claude Code picks the most common framework for the language it detects.
How do I prevent Claude Code from overwriting existing test files?
Add to your CLAUDE.md: "Before writing any test file, check whether it already exists. If it does, add new test cases to the existing file rather than replacing it. Never delete existing test cases." You can also enforce this with a PreToolUse hook that intercepts Write calls targeting tests/ and checks for existing content before allowing the write.
Can Claude Code achieve 100% branch coverage?
In practice, no — and 100% branch coverage is rarely the right goal. Claude Code will cover the branches it can infer from the source, which typically yields 80–90% line coverage on well-structured code. Branches that require specific infrastructure state (specific time of day, external service responses, OS signals) may require manual tests. Use pytest --cov=src --cov-report=term-missing to identify uncovered lines, then ask Claude Code to fill the gaps for specific uncovered branches.
Top comments (0)