How to Write Tests with Claude Code: Unit, Integration, and E2E
Nobody loves writing tests. But everyone loves having tests. There is a reason test suites are the first thing you check when inheriting a codebase, and the last thing you want to write yourself. Claude Code bridges that gap. It reads your source code, understands the business logic, and generates tests that cover happy paths, edge cases, error conditions, and boundary values. The tests it writes are not shallow mocks-everything stubs. They are meaningful tests that catch real bugs, structured the way a senior engineer would write them. Here is how to make test writing the easiest part of your entire workflow.
Why Claude Code Writes Better Tests Than You'd Expect
The first time you ask Claude Code to generate tests, you might expect generic boilerplate. What you actually get is surprisingly thorough. Claude Code does not just look at function signatures and guess. It reads the full source code, follows the control flow, and understands what the code is trying to accomplish. That means the tests it generates are informed by actual business logic, not just type signatures.
Where Claude Code really shines is edge cases. Human developers tend to write tests for the golden path and maybe a couple of error cases. Claude Code systematically considers null inputs, empty arrays, boundary values, off-by-one scenarios, concurrent access patterns, and type coercion pitfalls. These are the exact cases that cause production bugs, and they are the ones most often skipped when writing tests by hand.
The structure of the generated tests is clean, too. You get proper arrange-act-assert patterns, descriptive test names that read like documentation, and properly isolated tests that do not depend on execution order. Claude Code knows the conventions of every major testing framework -- Jest, Vitest, pytest, Go's testing package, RSpec, JUnit -- and generates idiomatic code for whichever one your project uses.
Perhaps the most impressive case is generating tests for code that has zero existing tests. Starting from nothing is the hardest testing challenge, and Claude Code handles it by reading the implementation, inferring the expected behavior, and building a complete test suite from scratch.
The Testing Workspace
Before you start generating tests, set up a Beam workspace that keeps everything visible and fast. The ideal testing workspace uses four tabs so you can monitor the entire feedback loop without switching contexts.
- Tab 1: Claude Code -- This is where you write prompts and Claude generates tests. Keep it focused on test generation.
- Tab 2: Test runner in watch mode -- Run
npm run test -- --watchorpytest --watchso tests execute automatically as Claude writes files. - Tab 3: App or server -- Keep your application running so you can verify behavior at the integration level when needed.
- Tab 4: Coverage report -- Run
npm run test:coverageto track your progress in real time.
Use Beam's split pane feature (⌘⌥⌃T) to put Claude Code and the test runner side by side. As Claude writes test files, you will see them appear in the runner output immediately. Green checkmarks rolling in while Claude is still typing -- that is the workflow you want.
Unit Tests: The Foundation
Unit tests are where you start, and they are where Claude Code delivers the most value per prompt. A single, well-crafted instruction like "Write unit tests for the CartService class. Cover all public methods, edge cases, and error conditions" is enough to produce a comprehensive test suite with organized describe/it blocks, meaningful assertions, and thorough coverage.
Claude Code generates test data factories instead of fragile inline test data. Instead of hardcoding { id: 1, name: "Widget", price: 9.99 } in every test, it creates helper functions like createMockProduct() and createMockCart() that you can reuse and extend. This makes your test suite maintainable from day one.
The sad path is where Claude Code really earns its keep. It tests what happens when input is invalid, when required fields are missing, when arrays are empty, when numbers are negative or zero, and when strings contain unexpected characters. These are the tests that catch the bugs your users would have found in production.
With your test runner in watch mode in the next tab, you can see each test appear and pass in real time as Claude writes them. It is a surprisingly satisfying feedback loop.
Integration Tests: Components Working Together
Integration tests are harder to write by hand because they require understanding how multiple parts of a system interact. Claude Code handles this naturally because it can read across multiple source files and understand the dependency graph. A prompt like "Write integration tests for the checkout flow: cart to payment to order creation to email notification" gives Claude the scope it needs to generate meaningful multi-step tests.
The generated tests include proper setup and teardown. Claude Code creates database fixtures, seeds test data before each test, and cleans up afterward. It handles API mocking at the right boundaries -- mocking external services while letting internal components interact with each other, which is exactly how integration tests should work.
One of the subtler skills Claude Code brings is knowing when to mock and when to use real implementations. It understands that mocking your own code too aggressively defeats the purpose of integration testing. External APIs, payment gateways, and email services get mocked. Your own database layer, validation logic, and business rules use real implementations.
Pro Tip: Control What Gets Mocked
Be explicit in your prompt about which dependencies to mock and which to use real. For example: "Mock the payment gateway but use the real database" or "Mock all external HTTP calls but test the full service layer with real implementations." This gives Claude Code clear boundaries and produces more useful integration tests.
E2E Tests: The Full User Journey
End-to-end tests simulate real user behavior from start to finish. Claude Code generates Playwright, Cypress, or Selenium tests depending on your project setup. A prompt like "Write Playwright E2E tests for the user registration flow: sign up, verify email, first login, onboarding" produces a complete test file with page navigation, form interactions, assertions, and proper waiting for async operations.
Claude Code generates page objects that encapsulate DOM selectors and common actions. This means your E2E tests read like user stories rather than CSS selector soup. It handles the async complexity that makes E2E tests brittle -- waiting for elements to appear, waiting for navigation to complete, waiting for API responses to return -- using the proper framework-specific patterns.
It also adds accessibility checks within the E2E flow. Claude Code will assert that form fields have proper labels, that focus management works correctly after navigation, and that error messages are announced to screen readers. These are checks that most teams skip entirely, but they come for free when Claude writes your E2E tests.
Run E2E tests in a dedicated Beam tab while Claude writes them. Playwright's headed mode lets you watch the browser automation happen in real time, which is both useful for debugging and deeply satisfying to watch.
Testing React Components
React component testing has its own set of best practices, and Claude Code knows them cold. Ask it to "Write tests for the UserDashboard component using Testing Library" and you get tests that follow the Testing Library philosophy: test what the user sees and does, not implementation details.
The generated tests use getByRole, getByText, and getByLabelText instead of reaching for test IDs or CSS class selectors. User interactions use userEvent instead of fireEvent, which more accurately simulates real browser behavior. Async state changes are handled with waitFor and findBy queries.
Claude Code generates three categories of component tests: render tests that verify the component displays correct content in different states, interaction tests that simulate clicks, typing, and form submissions, and async tests that verify loading states, data fetching, and error handling. Together, these give you confidence that the component works as users expect.
Testing API Endpoints
API testing is one of Claude Code's strongest areas because the contract is explicit. Ask it to "Write Supertest tests for all /api/users endpoints" and it generates comprehensive coverage for every route: happy path responses, validation error handling, authentication failures, authorization checks, and edge cases like malformed request bodies or missing required fields.
The test setup is production-quality. Claude Code generates proper database seeding to create test users, authentication token generation for protected routes, and cleanup logic that runs after each test. Each endpoint gets its own describe block with tests for every HTTP method it supports and every status code it can return.
What would take an afternoon of tedious work -- writing tests for a dozen REST endpoints with all their variations -- takes minutes. Claude Code understands RESTful conventions, so it knows to test pagination parameters, sorting, filtering, and field selection if your API supports them.
Going from 0% to 80% Coverage
The Untested Codebase Strategy
If you are staring at a codebase with zero test coverage, here is the strategy that turns a months-long effort into a days-long sprint:
- Identify critical modules first. Ask Claude Code to analyze your codebase and identify the highest-risk modules -- the ones with the most business logic, the most dependencies, or the most bug-prone history.
- Generate tests for those first. Start with the modules that give you the biggest safety net. This is where your testing ROI is highest.
- Run the coverage report. Check your coverage output in the fourth tab. Identify the gaps.
- Target the gaps. Use prompts like "Write tests to cover the uncovered branches in UserService." Claude Code reads the coverage data and targets exactly what is missing.
- Repeat until you hit your target. Each round gets faster because the remaining uncovered code is smaller and often follows similar patterns.
This strategy works because Claude Code can generate tests far faster than any human. The bottleneck shifts from writing tests to reviewing and refining them. A codebase that would take a team weeks to bring under test coverage can reach 80% in a matter of days with this approach.
Memory File: Testing Conventions
To get consistent, project-specific tests from Claude Code every time, add a testing section to your CLAUDE.md memory file. This tells Claude Code exactly how your team writes tests, so every generated test file matches your conventions without manual edits.
Example CLAUDE.md Testing Section
- Testing framework: Vitest with @testing-library/react for components
- File naming:
*.test.tscolocated next to source files - Mock policy: Mock external APIs and third-party services. Use real implementations for internal modules and database (test DB).
- Test data: Use factory functions in
tests/factories/. Never hardcode test data inline. - Coverage threshold: 80% branches, 85% statements. Fail CI below threshold.
- Assertion style: Use
expect().toBe()for primitives,expect().toEqual()for objects. Prefer specific matchers (toHaveBeenCalledWith) over generic ones.
With these conventions stored in memory, Claude Code produces test files that are ready to commit. No reformatting, no renaming, no restructuring. The tests match your codebase from the first generation.
Make Test Writing Effortless
Download Beam and watch Claude Code generate comprehensive tests while you focus on building features.
Download Beam for macOSSummary
Test writing does not have to be the part of development you dread. With Claude Code and a well-organized Beam workspace, you can generate unit tests, integration tests, E2E tests, component tests, and API tests that are thorough, well-structured, and ready to commit. The key practices to remember:
- Set up a four-tab testing workspace in Beam: Claude Code, test runner in watch mode, app server, and coverage report
- Start with unit tests for comprehensive foundation coverage, then layer on integration and E2E tests
- Be explicit about mock boundaries -- tell Claude what to mock and what to keep real
- Use the 0% to 80% strategy for untested codebases: prioritize critical modules, target coverage gaps, iterate
- Store testing conventions in CLAUDE.md so every generated test matches your team's standards
- Let the test runner watch mode give you real-time feedback as Claude writes tests
The gap between "nobody wants to write tests" and "everyone wants to have tests" just got a lot smaller.