CLAUDE LABJP
FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as OctoberFORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Articles/Claude Code
Claude Code/2026-05-04Intermediate

Improving Test Coverage Incrementally with Claude Code

Learn how to use Claude Code to identify uncovered functions, generate targeted tests, and incrementally improve test coverage in TypeScript + Vitest projects.

claude-code129testing6vitest2typescript11test-coverage

When test coverage drops below 40%, what's your first move? A lot of developers start by scanning the codebase manually, relying on gut feel to decide what needs testing next. That instinct isn't entirely wrong — experienced developers often know which code is risky — but it doesn't scale, and it's easy to miss the edge cases that actually cause bugs in production.

If you can feed your coverage report directly to Claude Code and let it figure out what to test and why, you can skip most of that manual analysis. In one of my personal projects, I went from 45% to 78% branch coverage in under two hours using the workflow described here. The generated tests still needed review before committing — that part's non-negotiable — but the cognitive overhead of knowing where to start was essentially eliminated.

Here's how it works in practice.

What Test Coverage Actually Measures

"Coverage" means different things depending on which metric you're looking at. Vitest (backed by c8 or istanbul) tracks four main types:

  • Statements: percentage of executable statements that ran during tests
  • Branches: percentage of conditional branches (both sides of if/switch/ternary) that were exercised
  • Functions: percentage of defined functions that were called
  • Lines: percentage of source lines that were executed

Branch coverage is the one I find most meaningful for finding real bugs. A function can show 100% function coverage — meaning it was called — while its error-handling path never executes. The null checks, boundary values, and exception handlers are exactly where production incidents tend to originate, and branch coverage is what surfaces those gaps.

Statement and line coverage can give a misleadingly optimistic picture. A single happy-path test can push both metrics well above 80% in a file that has almost no meaningful verification happening.

To start tracking branch coverage in Vitest, add this to vitest.config.ts:

// vitest.config.ts
import { defineConfig } from 'vitest/config'
 
export default defineConfig({
  test: {
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
      // Be explicit about what to include to avoid inflated numbers
      include: ['src/**/*.ts', 'src/**/*.tsx'],
      exclude: ['src/**/*.d.ts', 'src/**/index.ts'],
      // Optional: fail CI if coverage drops below these thresholds
      thresholds: {
        branches: 60,
        functions: 70,
      },
    },
  },
})

Running npx vitest run --coverage generates two things: an HTML report under coverage/ for visual review, and a coverage/coverage-summary.json that Claude Code can read directly. The JSON is the key input for everything that follows.

Analyzing the Coverage Report with Claude Code

Instead of manually cross-referencing uncovered lines with your source files, you can hand that analysis entirely to Claude Code. The prompt is straightforward:

Read coverage/coverage-summary.json and list all files
where branch coverage is below 60%.
For each file, include the file path, current branch coverage percentage,
and a brief note on which types of conditional paths are likely untested.

Claude Code reads the JSON, then opens the actual source files to understand the logic in context. It doesn't just sort the numbers — it often provides reasoning like "this error path is likely untested because all the existing tests use valid inputs" or "the null branch here is never exercised because the mock always returns a real object."

That contextual layer is what makes this different from just sorting coverage-summary.json yourself. It takes roughly 30 seconds for a project with 20-30 files, and the output gives you a prioritized list of where to focus.

Requesting Test Generation File by File

Once you have your priority list, generate tests one file at a time — not all at once. Asking Claude Code to cover ten files in a single request reduces output quality and makes it harder to verify that the generated tests match your existing conventions.

Here's a prompt structure that consistently produces usable output:

Branch coverage for src/utils/pricing.ts is at 42%.
Read this file and the existing __tests__/pricing.test.ts,
then write Vitest test cases that cover the untested branches.

Requirements:
- Append to the existing test file without duplicating describe blocks
- Cover edge cases: zero values, negative numbers, invalid currency codes
- Use describe/it blocks with descriptive names
- Write specific assertions — avoid vague ones like expect(result).toBeTruthy()
- Include the expected output as a comment if the value isn't obvious

Two things here are worth emphasizing. First, sharing the existing test file prevents naming conflicts. Second, the explicit instruction about assertion specificity matters more than it seems. Without it, you'll occasionally get tests like expect(result).not.toBeNull() that technically pass without verifying anything useful. That last line pushes Claude Code toward assertions like expect(result).toEqual({ amount: 0, currency: 'JPY' }), which actually tell you when behavior changes.

A Full Workflow: TypeScript + Vitest in Practice

Here's the complete cycle, step by step:

Step 1: Generate the coverage report

npx vitest run --coverage
# This outputs coverage/coverage-summary.json

Step 2: Get a prioritized list from Claude Code

Check coverage/coverage-summary.json.
List the 5 files with the lowest branch coverage.
For each, describe 2-3 specific scenarios that would
most improve the numbers if tested.

Step 3: Generate tests starting with business logic

Business logic first, then error handling, then utilities. This ordering gives you the highest ROI early — tests that catch logic errors are more likely to prevent actual bugs than tests that verify utility formatting.

Step 4: Run the generated tests immediately

npx vitest run __tests__/pricing.test.ts

Don't let generated tests accumulate without running them. Failures are much easier to diagnose one file at a time.

Step 5: Feed failures back to Claude Code

This test is failing with the following error:
[paste the full error output]

Identify the root cause and fix the test.

In my experience, about 20-30% of generated tests need some correction on the first run. The failures are usually mock configuration issues or incorrect expected values, not fundamental misunderstandings of the code.

Step 6: Re-run full coverage and confirm improvement

After covering two or three files, run npx vitest run --coverage again. Seeing the numbers move is motivating — and it confirms that the new tests are actually reaching the branches they were intended to cover.

Common Pitfalls and How to Handle Them

Mock setup that doesn't match your actual types

For files with external dependencies — API clients, database access, file system operations — Claude Code sometimes generates vi.mock() configurations that don't line up with your actual interfaces. The fix is simple: tell it. "This mock causes a TypeScript error. The actual interface is in src/types/api.ts." Passing the type definition resolves the issue almost every time.

Tests that pass but verify nothing meaningful

This is the most common quality issue with generated tests. Watch for assertions like expect(result).toBeTruthy() or expect(fn).not.toThrow() without checking what was returned. When you spot one, ask: "What should calculateTotal return when both quantity is 0 and discount is applied? Write an assertion for that specific case." The follow-up is usually precise.

Duplicate describe or it names causing confusing failures

If you're appending to an existing test file and the generated code reuses a describe block name that already exists, Vitest will run both but the output becomes hard to read. Passing the existing test file content in your prompt prevents this. A cat __tests__/target.test.ts in your terminal and pasting the output into Claude Code is enough.

Async tests hitting real services

Occasionally, Claude Code generates async tests that skip mock setup and make actual network or database calls. The symptom is tests that are unexpectedly slow or that fail depending on network availability. When you see this, ask explicitly: "This test is making a real HTTP call. Add vi.mock() to intercept the request and return a fixture instead."

Over-indexing on the coverage number

Branch coverage is a proxy for test quality, not a guarantee of it. It's possible to reach 90% branch coverage with tests that don't prevent any real bugs — if the assertions are weak. Use the coverage number to find untested code, but treat the review of generated tests as a separate and necessary step. The goal is tests that break when behavior changes, not tests that just run.

For deeper patterns on test structure and maintainability, the Vitest and Jest unit test automation guide covers test organization in detail, and the TDD workflow introduction is worth reading if you want to build coverage up from scratch on a new feature rather than retrofitting tests on existing code.

Your Next Step

Run npx vitest run --coverage in a project today and ask Claude Code: "List the five files with the lowest branch coverage and suggest what to test first in each." The analysis takes five minutes or less.

Coverage improvement starts with measurement. Once measurement is cheap, improvement follows naturally. With Claude Code handling the analysis and first-draft test generation, the measurement-to-fix cycle becomes fast enough to actually maintain as a habit.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Claude Code2026-07-03
Five Minutes of Silence, and Something Retries on Your Behalf — Rethinking Retry Ownership After the Streaming Idle Watchdog Became a Default
Claude Code's streaming idle watchdog is now on by default, quietly adding another retrying layer to your stack. This article inventories the four layers (SDK, wrapper, watchdog, scheduler), computes worst-case attempt amplification, and shows how to collapse retry ownership into a single layer.
Claude Code2026-04-21
Resolving tRPC Type Errors in 15 Minutes with Claude Code — A Practical Workflow
When tRPC type errors cascade across files, how can Claude Code help you fix them fast? Router design, client integration, and the pitfalls I hit in real projects — distilled into a practical workflow.
Claude Code2026-03-28
Running Claude Code × Playwright E2E Tests in Production — Measured Numbers, Pitfalls, and Decision Criteria from 6 Sites
Implementation patterns for running E2E tests with Claude Code and Playwright in production, plus measured numbers from operating six sites in parallel: flaky-test rate 15.3% to 0.42%, Page Object generation 4 hours to 18 minutes. Includes the Cloudflare Workers and Pages preview deployment pitfalls, and a decision framework that indie developers can use to judge whether E2E tests are worth the investment.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →