Coverage Gap Finder
Run the project's coverage tool and identify the highest-value untested paths — error branches, edge cases, and critical modules — then propose specific test cases for each gap. Use when you have a coverage report but don't know where new tests will pay off most.
npx agentscamp add skills/coverage-gap-finderInstall to ~/.claude/skills/coverage-gap-finder/SKILL.md
A skill that runs the project's existing coverage tool and turns the report into a ranked plan: it parses per-line and per-branch data, weights gaps by risk — uncovered error handlers, unguarded edge cases, critical modules — names the exact untested branch, and proposes concrete test cases for each, prioritizing risk reduction over a 100% badge.
Turn a raw coverage report into a ranked, actionable plan. This skill runs the project's existing coverage tool, reads the per-line and per-branch data, and surfaces the gaps that actually matter — uncovered error handlers, unguarded edge cases, and critical modules with thin coverage — rather than nudging an arbitrary percentage upward. For each gap it proposes concrete, named test cases you can hand straight to a scaffolder. The goal is risk reduction per test written, not a green 100% badge.
When to use this skill
- You have (or can generate) a coverage report but don't know which untested lines are worth testing first.
- A module is business-critical and you want to confirm its error paths and edge cases are exercised.
- You're triaging tech debt and need a prioritized list of test gaps instead of a wall of red lines.
NOTE
Line coverage measures executed lines, not correct behavior. A function can be 100% covered by a test that asserts nothing. Treat coverage as a map of blind spots, not a quality score — prioritize gaps by blast radius, then verify the new tests actually assert something.
Instructions
- Detect the test stack and coverage tool. Do not guess — inspect the project:
- JS/TS:
package.jsonforvitest --coverage/jest --coverage(look forcoveragescripts or ac8/nyc/@vitest/coverage-v8dep). - Python:
pytest --cov(pytest-cov),coverage run, config inpyproject.toml/.coveragerc. - Go:
go test -coverprofile. Java: JaCoCo. Match whatever already runs in CI.
- JS/TS:
- Generate machine-readable coverage. Run the tool to emit a structured report —
--coverage --coverage.reporter=json(Vitest),--cov --cov-report=json(pytest), or-coverprofile=cover.out(Go). Parse the JSON/profile, not the pretty terminal table; you need per-file branch and line data. (For Vitest,--coverage.reporter=jsonwritescoverage/coverage-final.jsonwith per-file branch and line data; the unrelated--reporter=jsonis a test-result reporter — pass/fail/timing — and won't produce coverage.) - Rank gaps by value, not size. For every uncovered region, weight it by signals — uncovered
catch/except/error returns,if/switchbranches with no covered alternative, input validation, and modules central to the app (auth, payments, parsing, persistence). Down-rank generated code, trivial getters, and config glue. A 60%-covered payment module outranks a 40%-covered logging helper. - Locate the exact untested paths. For each top gap, read the source and name the specific uncovered branch (file, function, line range) and why it's untested — e.g. "the
429retry branch is never hit because no test injects a rate-limit response." - Propose concrete test cases. For each gap, write one bullet per missing case stating the input/condition and the expected behavior — not "add tests for error handling," but "call
withRetrywith a stubbed client that throwsRateLimitErrortwice then succeeds; assert it retries and returns the value." Hand these totest-scaffolderto generate. - Verify the baseline and report. Re-run coverage to confirm the numbers you're quoting are current, then output a prioritized gap list (highest value first) with file, current coverage, the risk, and the proposed cases. Flag any module you couldn't analyze (e.g. excluded from the report, untestable without a fixture).
WARNING
Never chase 100%. Forcing coverage on glue code, framework boilerplate, or unreachable defensive branches produces brittle tests that assert nothing and slow the suite. Stop when the remaining gaps are low-risk — and say so explicitly in the report.
Examples
Running coverage on a Vitest project and triaging the JSON report:
npx vitest run --coverage --coverage.reporter=json
# reads coverage/coverage-final.jsonOutput — a prioritized gap list, not a flat percentage:
Coverage: 78% lines / 64% branches (412/640 branches)
HIGH VALUE
1. src/payments/charge.ts — 71% lines, 50% branches
Risk: the declined-card and idempotency-key-reuse branches are never exercised.
Propose:
- charge() with a gateway stub returning `card_declined` → asserts no DB write, throws PaymentError
- charge() called twice with the same idempotency key → asserts the second call returns the first result, no double charge
2. src/auth/verifyToken.ts — 83% lines, 60% branches
Risk: expired-token and malformed-signature paths uncovered.
Propose:
- verifyToken() with a token whose `exp` is in the past → asserts throws TokenExpiredError
- verifyToken() with a tampered signature → asserts throws InvalidSignatureError
MEDIUM VALUE
3. src/parse/csv.ts — 88% lines
Risk: quoted-field-with-embedded-comma branch untested.
Propose:
- parseCsv('"a,b",c') → asserts two fields ["a,b", "c"]
SKIP (low value)
- src/logger.ts (45%) — thin wrapper over console; defensive branches only.
- src/generated/*.ts — codegen output, exclude from coverage instead.
Hand the HIGH and MEDIUM cases to test-scaffolder, then re-run coverage to confirm the branches now flip green.
Related
- Test ScaffolderScaffold a test file with sensible cases for a given module or function. Use when adding tests to untested code and you want a fast, structured starting point.
- Test EngineerUse this agent to write and improve automated tests — unit, integration, and edge cases. Examples — adding coverage to an untested module, writing regression tests for a bug, designing a test plan.
- Contract Test DesignerDesign consumer-driven contract tests between services so an API provider can't break its consumers unnoticed — without slow, flaky full end-to-end environments. Use when independent services or teams integrate over an API, when integration bugs only surface in staging or prod, or when E2E suites are too slow and brittle to catch breaking API changes.
- Integration Test DesignerDesign integration tests that exercise components against REAL collaborators — actual database, queue, HTTP boundary — at a deliberately chosen seam, instead of a unit suite that mocks everything or a slow flaky full E2E. Use when bugs slip past green unit tests, when wiring or contracts between layers break in production, or when a mocked DB test passes but the real query/migration/serialization fails.
- Mutation Test RunnerMeasure whether a test suite actually catches bugs by running mutation testing — introduce small faults into the code and check which ones a test kills versus which slip through silently. Use when line coverage is high but bugs still ship, when you suspect tests assert weakly, or to find the exact assertions a suite is missing.
- Property Test DesignerDesign property-based tests — generate hundreds of random inputs and assert invariants that must hold for ALL of them — to surface the edge cases hand-picked examples never reach. Use when code has a large input space (parsers, serializers, encoders, math, data transforms), when a bug keeps slipping through despite green example tests, or when you can't enumerate every case worth checking.
- How to Test AI-Generated CodeAI writes the code; tests decide whether to trust it. The verification stack for agent-written changes — contracts, generated tests, and the review that's left.