Test Coverage Analysis

SkillDev tools

Use after writing tests to assess coverage quality across structural, mutation, requirements, and API/integration dimensions; organized knowledge for choosing and interpreting coverage analyses.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Test Coverage Analysis skill

What this skill tells your AI

The instructions your AI receives, as published by neolabhq/context-engineering-kit in skills/test-coverage/SKILL.md and read by ahel’s review.

A reference manual for choosing, applying, and interpreting test-coverage analyses on an existing test suite.

This skill is a knowledge reference, not a procedure. It does not tell you when to write tests or which test types to design — that is the job of design-testing-strategy. It tells you, once tests exist, which mechanical signal best measures what those tests do (and do not) exercise, and how to read that signal honestly.

What Coverage Analysis Is

Test coverage analysis is the post-hoc measurement of how thoroughly a test suite exercises a software artifact along one or more axes. It answers the question "what did my tests actually touch?" — for some specific definition of "touch."

The word "coverage" is overloaded. It can mean any of:

  • Structural / code coverage — which lines, statements, branches, conditions, or paths in the source code were executed (measured by instrumentation).
  • Mutation coverage — what proportion of deliberately-injected source faults the test suite detects (measured by re-running the suite against mutated code).
  • Requirements / feature coverage — which acceptance criteria, user stories, or specification clauses have at least one verifying test (measured by traceability).
  • API / integration coverage — which endpoints, methods, status codes, contract interactions, and schema fields are exercised (measured by request/response inspection).
  • Specification-domain coverage — equivalence classes, boundary values, parameter combinations, state transitions, error paths (measured by analyzing test inputs against a model).

Category correction: coverage is not a test type

Mutation testing, MC/DC, branch coverage, RTM linkage, contract coverage, and schema coverage are measurements about an existing test suite. They are not test types in the way unit, integration, e2e, contract, or smoke tests are.

  • "Should I write a unit test or a mutation test?" is a malformed question. The correct framing is: "I already have unit/integration tests; should I additionally run mutation analysis against them?"
  • Mutation tools generate variants of the source and re-execute the existing suite. They produce a score, not new tests.
  • MC/DC and branch coverage are reports computed from instrumented runs of the existing suite.
  • RTM linkage is a property of test metadata (tags, IDs), not a separate execution.

If a "test strategy" places mutation testing alongside unit / integration / e2e, that strategy has confused what to test with how to measure the tests. The two questions are orthogonal.

The asymmetry principle

Low coverage is strong evidence of weak testing. High coverage is weak evidence of strong testing.

Coverage is necessary-but-not-sufficient. A test can execute a line without asserting anything meaningful; 100% line coverage is routinely achievable with zero assertions (thinkinglabs.io, codeintelligently.com). Use coverage as a tripwire, not a trophy. Once a coverage percentage becomes a target, it ceases to be a good metric (Goodhart's law applied to testing; see Optivem Journal).

What coverage analysis is NOT

  • NOT a measure of test quality. Lines can execute without assertions.
  • NOT a measure of correctness. Coverage proves the test ran, not that it would have failed on a bug.
  • NOT a synonym for "well tested". Mutation testing routinely refutes 100%-coverage-with-no-assertions suites.
  • NOT a substitute for risk-based test selection per ISO/IEC/IEEE 29119.
  • NOT a target. Treat as a floor and a trend, never as the goal itself.

Per-Type Structure

Every coverage type in this skill is documented in the same six sub-fields, in this order:

  1. Definition — what it measures.
  2. What it does NOT measure — its limits / blind spots.
  3. Typical tools — per ecosystem.
  4. When to use vs skip — applicability heuristics.
  5. Targets / thresholds & pitfalls — defensible numeric ranges (always with the risk caveat) and common gaming patterns.
  6. Cost-benefit ROI — order-of-magnitude cost vs the signal you actually buy.

Scan any section by these headings.


Structural / Code Coverage

Measured by instrumenting the compiled or interpreted program and recording which structural elements (lines, statements, branches, conditions, paths) the test suite executes.

Line / Statement Coverage

  • Definition. Percentage of source-code lines (or statements) executed at least once.

  • What it does NOT measure. Whether branches were taken in both directions. Whether assertions verified the result. Whether boundary values were tested. Multiple statements on one line distort the metric (Metridev).

  • Typical tools.

    EcosystemTool
    JS/TSIstanbul / nyc (built into Jest, Vitest, Karma); --coverage flag
    PythonCoverage.py + pytest-cov; supports branch mode
    JVMJaCoCo — bytecode instrumentation, industry standard
    C/C++gcov / lcov / gcovr, llvm-cov
    Gogo test -cover, go tool cover (build-cover added integration-test mode in Go 1.20)
    .NETCoverlet (open-source default), JetBrains dotCover, AltCover. OpenCover is in maintenance mode — prefer Coverlet / dotCover / AltCover (NDepend guide)
    RubySimpleCov
    Swift / Obj-CXcode built-in (llvm-cov backend)
    Rustcargo-llvm-cov, cargo-tarpaulin
    Report formatsCobertura XML, LCOV, Clover; aggregators: Codecov, Coveralls, SonarQube
  • When to use vs skip. Always-on; cost is near-zero (a CI flag). Never use as a quality goal in itself.

  • Targets / thresholds & pitfalls. 70–85% is typical for general-purpose code (Qt blog). Apply only with the risk caveat (see Risk-Based Interpretation below). Tests with no assertions still count lines as covered. Single-line if (x) doA(); else doB(); shows 100% statement coverage with only one branch exercised. Snapshot-only tests inflate numbers without verifying behavior.

  • Cost-benefit ROI. Very high — cost near-zero, value is a tripwire on regression in test reach.

Branch / Decision Coverage

  • Definition. Percentage of decision branches (true/false outcomes of if, while, for, ?:, switch cases) executed.
  • What it does NOT measure. Compound-condition independence (A && B taken true might never test A=true, B=false). Order of evaluation. Loop iteration counts. Assertion strength.
  • Typical tools. Same as line coverage; enable with --branch (coverage.py), branch mode (Istanbul is branch-aware by default), JaCoCo reports branches natively. Strictly stronger than line/statement coverage (Graph AI).
  • When to use vs skip. Default for any non-trivial logic. Prefer branch over line as the primary structural metric.
  • Targets / thresholds & pitfalls. 70–80% branch is a "respectable" target for business apps (Lead With Skills) — always paired with the risk caveat. Compound conditions hide gaps: if (A || B) achieves 100% branch coverage with only one true-evaluating sub-condition and one false branch overall.
  • Cost-benefit ROI. High — best single structural metric for general-purpose code (LinearB).

Condition Coverage

  • Definition. Every Boolean sub-condition in every decision has taken both true and false at least once.

  • What it does NOT measure. Whether each sub-condition independently affects the outcome (that is MC/DC). Does not require all combinations.

  • Typical tools. Same toolchains as branch coverage; many report condition coverage as a separate column.

    EcosystemTool
    JVMJaCoCo (condition counters in branch reports)
    C / C++gcov/gcovr (--branch-counts), Qt Coco
    .NETCoverlet (condition coverage via Cobertura output)
  • When to use vs skip. Informative for code with compound expressions; rarely useful as a CI gate on its own.

  • Targets / thresholds & pitfalls. Achievable without exercising every combination. if (A && B) hits 100% condition coverage with {A=T,B=F} and {A=F,B=T} — neither makes the decision true. Treat as a diagnostic, not a gate.

  • ROI: Medium — useful diagnostically when investigating why branch coverage looks high but bugs persist; not a gate.

MC/DC — Modified Condition/Decision Coverage

  • Definition (per Wikipedia):

    1. Every entry/exit point invoked at least once.
    2. Every decision has taken every outcome at least once.
    3. Every condition in a decision has taken every outcome at least once.
    4. Each condition has been shown to independently affect the decision's outcome (holding the other conditions fixed).

    For n conditions, MC/DC is achievable with n+1 to 2n tests via independence pairs — vastly cheaper than the 2^n of exhaustive multiple-condition coverage (LDRA).

  • What it does NOT measure. Loop iteration counts, data values, integration paths, assertion strength.

  • Typical tools. LDRA TBvision, Rapita RapiCover, VectorCAST, Razorcat TESSY, Qt Coco, Parasoft C/C++test. Mostly commercial — open-source MC/DC is rare.

  • When to use vs skip. When mandated by a standard (DO-178C DAL A, ISO 26262 ASIL D, IEC 62304 Class C high-risk modules, EN 50128 SIL 4, IEC 61508 SIL 4). Outside regulated domains, branch coverage + mutation testing covers the same intent at lower cost.

  • Targets / thresholds & pitfalls. 100% by definition in regulated domains. Short-circuit evaluation in C-like languages can make some independence pairs unreachable; compiler optimizations can collapse conditions, so coverage builds must disable optimization — meaning the coverage-build binary is not the release-build binary, an acknowledged regulatory risk (Verifysoft).

  • Cost-benefit ROI. Very high cost (specialist toolchain + labor + documentation overhead); high value only where required by law/standard.

Function / Method Coverage

  • Definition. Percentage of declared functions/methods invoked at least once.

  • What it does NOT measure. Anything about the bodies of those functions.

  • Typical tools. Reported by most structural-coverage tools as a side column.

    EcosystemTool
    JVMJaCoCo (method counter)
    .NETCoverlet (methods column)
    Pythoncoverage.py (report -m granularity), pytest-cov
    JS/TSIstanbul (functions metric in lcov / json-summary)
  • When to use vs skip. As a quick "did I forget a module?" check; never as a primary metric.

  • Targets / thresholds & pitfalls. Often deceptively high — many functions are entered by happy-path tests with no error-path coverage inside.

  • ROI: Low — informational only; useful as a "module forgotten?" tripwire, not a gate.

Path Coverage

  • Definition. Percentage of unique linearly-independent paths through a function. Bounded by cyclomatic complexity V(G) = decisions + 1 (Cyclomatic complexity).

  • What it does NOT measure. Anything practical for non-trivial functions — N decisions yields 2^N paths, unbounded for loops.

  • Typical tools. Some commercial safety-critical tools report basis-path counts; rarely a CI artifact.

    EcosystemTool
    Safety-critical C/C++LDRA TBvision, VectorCAST (basis-path metrics)
    Any / complexity proxylizard, radon, SonarQube (cyclomatic complexity as a bound, not a path metric)
  • When to use vs skip. Rarely as a coverage target. Cyclomatic complexity is more useful as a complexity signal that bounds the minimum number of tests needed to exercise distinct flows.

  • Targets / thresholds & pitfalls. Combinatorial explosion. Most production code is uncovered at path-coverage level and that is acceptable.

  • ROI: Low for production code; meaningful only inside very small, very high-criticality functions — outside that, use complexity as a signal and stop.


Mutation Testing as Coverage Analysis

Reminder: Mutation testing is a coverage analysis of an existing test suite. It is not a test type. It produces a score and a list of survived mutants; it does not produce new tests. You apply it to your unit / integration suite, not instead of it.

Definition

Mutation testing introduces small, syntactic modifications ("mutants") to the source and re-runs the existing test suite against each mutant. If at least one test fails for a given mutant, the mutant is killed (the suite detected the fault). If all tests pass, the mutant survived (the suite is blind to that change). It measures test-suite fault-detection power, not source-code reach (Stryker docs).

Typical mutation operators:

  • Arithmetic+-, */, ++--.
  • Conditional / relational<<=, ==!=, &&||.
  • Boolean / negationtruefalse, remove !.
  • Statement removal / block deletion.
  • Return valuereturn xreturn null / return "".
  • Increment / decrement of literal constants.
  • Conditional boundary>>=.

Mutant states (Stryker docs)

StateMeaning
KilledAt least one test failed on the mutant. Suite detected the fault.
SurvivedAll tests passed on the mutant. Suite is blind.
No coverageNo test executed the mutated code (orthogonal gap — code itself is untested).
TimeoutTests hung; usually counted as a kill (the suite did observe abnormal behavior).
Compile error / runtime errorMutant is syntactically/semantically invalid; usually filtered.
IgnoredFiltered by config (generated code, glue, etc.).

Score: mutation_score = killed_mutants / (total_mutants - equivalent_mutants - errors). Some tools also report a "killed%" relative to covered mutants only.

What it does NOT measure

  • Dead-code regions — appear as no coverage, identical to "line not covered."
  • Semantic correctness of assertions — a wrong-but-strict assertion still kills mutants.
  • Boundary data values — operator mutants approximate this but do not replace BVA.
  • Equivalent mutants — variants that produce identical observable behavior. Detection is undecidable in general; manual review is the only certain method. Modern tools (Stryker TypeScript Checker, PIT with Major) reduce these heuristically. Do not chase 100% mutation score — equivalents make it asymptotically unattainable (Stryker docs).

Typical tools

EcosystemTool
JS / TSStryker (StrykerJS) — TypeScript checker plugin filters compile-error mutants
.NET (C#)Stryker .NET; documented in Microsoft Learn
Java / JVMPIT (Pitest) — reference standard for JVM; Major Mutator for research
Pythonmutmut, Cosmic Ray, MutPy
Gogo-mutesting, ooze
PHPInfection
Rubymutant
Rustcargo-mutants
C / C++Mull — LLVM-based
ScalaStryker4s

When to use vs skip

Apply when:

  • Suite is already structurally mature (typically >80% branch coverage). On a sparse suite, mutation results are dominated by no coverage and you learn nothing new beyond what structural coverage already shows.
  • Artifact is pure-logic core — financial calculations, security-critical validation, parsers, encryption, authorization decisions.
  • Criticality is high enough that suite blind spots represent material risk.

Skip when:

  • Glue code, controllers, framework wiring — operators generate noise on declarative constructs.
  • UI rendering — equivalents dominate.
  • Configuration, DTOs, declarative serialization.
  • Brand-new suite still being built up.
  • Tight CI feedback loop where N×suite runtime is prohibitive (mitigate with incremental analysis, not by giving up coverage).

Targets / thresholds & pitfalls

Stryker defaults (config): high: 80, low: 60, break: null. Set break to fail the build below a floor. Apply with the risk caveat: 60–80% on a mature unit suite over pure-logic core is a reasonable starting point; never on glue code. Common pitfalls: chasing equivalents (asymptote), running on UI/config (noise), running on shallow suites (re-reports what coverage already shows).

Cost-benefit ROI

  • Cost. CPU-quadratic-ish. A 60-second suite generating 1,000 mutants is up to 1,000 × 60s without optimization. Modern tools mitigate via incremental analysis, per-mutant test selection, and parallel runners.
  • Benefit. Catches missing assertions and over-mocked tests that structural coverage cannot detect. It is the only practical coverage technique that scores assertion strength — the chief failure mode of "100% coverage with no assertions" (codeintelligently.com).
  • CI pattern. Incremental mutation on PR diff; nightly full run on critical modules (oneuptime; see also research roundup at greg4cr.github.io).

Relationship to structural coverage

Mutation testing subsumes and supplements structural coverage:

  • A mutant in unreachable code is no coverage — identical signal to "line not covered."
  • A mutant in covered code that survives — "covered but not meaningfully verified" — is invisible to structural coverage.

Requirements / Feature Coverage

Definition

Mapping between specification artifacts (requirements, user stories, acceptance criteria, regulatory clauses) and verifying tests:

requirements_coverage = requirements_with_>=1_passing_test / total_requirements

The foundational artifact is the Requirements Traceability Matrix (RTM) — a two-dimensional table correlating requirements to test cases (ISTQB Glossary). Enables:

  • Forward traceability — does every requirement have a test?
  • Backward traceability — does every test trace to a requirement?
  • Change impact analysis — when requirement X changes, which tests must be revisited?

What it does NOT measure

  • Whether the test is correct — a passing test against the wrong assertion still ticks the RTM box.
  • Whether the requirement itself is complete — RTM coverage of 100% means nothing if the requirement set is missing scenarios.
  • Code reach — RTM is orthogonal to structural coverage.

Typical tools

ToolTypeNotes
Jira + Xray / Zephyr / Test ManagerALMStories ↔ tests linked in tickets
Polarion, IBM DOORS / DOORS Next, CodebeamerRegulated-domain ALMTier 1 for DO-178C / ISO 26262
Spreadsheet + tags in test namesLightweightWorks for small teams; deteriorates at scale
BDD scenario reportsBDD-alignedCucumber + Pickles report generator
@Tag("AC-123") style annotationsCode-levelJUnit / pytest tag-based linking to AC IDs

In BDD ecosystems, Gherkin Scenario: blocks are the unit of acceptance coverage; each AC ideally maps to one or more scenarios (the Cardinal Rule of BDD: one scenario, one behavior — Automation Panda). Tools: Cucumber (multi-language), SpecFlow (note: the active community fork is Reqnroll), behave (Python), pytest-bdd, Robot Framework, Behat (PHP).

Foundational standards: ISTQB Foundation Level treats RTM as a foundational artifact for systematic test design. ISO/IEC/IEEE 29119 parts 1–5 require traceability at the test-plan, test-design, and test-execution levels (ISTQB-to-29119 mapping in rcolomo.com). Regulatory traceability is mandatory in DO-178C, ISO 26262 Part 6 work products, IEC 62304 verification record, and FDA 21 CFR Part 820.

When to use vs skip

  • Use for anything with explicit acceptance criteria, regulated software, contract deliverables. Cheap when test names embed AC IDs (it("AC-3: rejects mismatched passwords")).
  • Skip for throwaway scripts and internal-only tooling without documented requirements.

Targets / thresholds & pitfalls

100% requirements coverage is a reasonable goal — every documented AC should have at least one test. The pitfalls are qualitative, not numerical:

  • Ceremony tax — heavy RTM tooling that demands manual updates dies of bitrot.
  • One-test-per-AC trap — a single test does not "cover" an AC if the AC has multiple equivalence partitions or boundaries.
  • Aspirational requirements — counting "tests planned" instead of "tests passing" produces fake green RTMs.
  • Silent invalidation — requirements churn breaks traceability unless link integrity is enforced.

Cost-benefit ROI

  • High for regulated / contract work where it is mandatory.
  • Medium for product teams using BDD with AC tags in test names — cost is near-zero, traceability is a CI artifact.
  • Low for solo / prototype work.

API / Integration Coverage

API coverage measures the integration surface — endpoints, methods, status codes, payload fields, and inter-service contracts — exercised by the test suite, independent of code coverage.

Endpoint Coverage

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
2k
Forks
157
Last commit
Aug 2026
Advanced
Catalog kind
skill
Gateway key
test-coverage-neolabhq
Source
github.com/neolabhq/context-engineering-kit