# What Is Code Coverage? — Line, Branch, Statement and What a Percentage Hides

Canonical: https://commareports.com/glossary/code-coverage
Published: 2026-09-11

> Code coverage measures which lines and branches a test run executed. The four metric types, the formats (lcov, Cobertura, JaCoCo), and why the number matters less than the drill-down.

# Code coverage

**Code coverage measures which parts of a codebase were executed while a
test suite ran, expressed as a percentage.** It answers "what did the
tests never touch", which is useful. It does not answer "was the
behaviour actually verified", which is the question people assume it
answers.

## The four metrics

| Metric        | Counts                                   | Catches                        |
| ------------- | ---------------------------------------- | ------------------------------ |
| **Line**      | Executed lines                           | Whole files nobody tests       |
| **Statement** | Executed statements (multiple per line)  | Chained expressions            |
| **Branch**    | Executed outcomes of each decision point | The untested `else`            |
| **Function**  | Called functions                         | Dead or forgotten entry points |

Branch is the informative one. An `if` with no `else` can show 100% line
coverage while the false path has never run once. A suite at 85% line
and 60% branch is a suite that exercises happy paths.

## The formats

- **lcov** (`lcov.info`) — the GNU tracefile format, emitted by
  Istanbul/nyc, `cargo-llvm-cov`, gcov and others
  ([share an lcov report](/share-lcov-report)).
- **Cobertura XML** — the format most CI systems and code hosts ingest
  for PR annotations ([share a Cobertura report](/share-cobertura-report)).
- **JaCoCo** — XML plus its own HTML tree on the JVM
  ([share a JaCoCo report](/share-jacoco-report)).
- **`coverage.py`** — XML, JSON and `htmlcov/` for Python
  ([share a coverage report](/share-coverage-report)).
- **SimpleCov** for Ruby, **`dotnet-coverage`** / Coverlet for .NET.

Every one of these has a machine format for the gate and an HTML tree
for the person who has to act on it.

## Why the HTML tree is the part that matters

A percentage in a PR comment tells a reviewer that coverage dropped. It
does not tell them _which_ new branch went untested, and that is the only
actionable fact in the report.

The HTML output is a file tree you click into, with each source line
coloured by whether it ran and each partially-covered branch flagged.
That drill-down is the report. Screenshotting the summary number
discards it, and so does a coverage badge.

The drill-down is also why the report is multi-file: the tree is one
generated page per source file, cross-linked. Copying `index.html` out
of `htmlcov/` gives a page whose every link 404s
([coverage report links broken](/fix/coverage-report-links-broken)).

## Coverage's blind spot

Coverage counts execution, not assertion. A test that calls a function
and asserts nothing gives it full coverage.
[Mutation testing](/glossary/mutation-testing) is the direct answer: it
changes the code and checks whether any test notices. Suites at 90%
coverage routinely score far lower on mutation score, and the gap is the
honest measure of the suite.

## Publishing the tree

Publish the whole coverage directory rather than the summary. Reviewers
open the file, see the uncovered branch, select it, and comment on that
line — instead of pasting a percentage into chat and arguing about
thresholds ([comment on HTML](/comment-on-html)).

## Try it

Comma is free — unlimited reports, unlimited commenters, unlimited
revision history.

**[Publish a coverage report →](https://commareports.com/)**

### Related

- [Share a coverage report](/share-coverage-report) · [JaCoCo](/share-jacoco-report) · [lcov](/share-lcov-report)
- [Mutation testing](/glossary/mutation-testing) · [JUnit XML](/glossary/junit-xml)
- [Coverage report links 404](/fix/coverage-report-links-broken) · [Glossary](/glossary)
