Code coverage · Comparison
Line coverage vs branch coverage
Line coverage asks whether each line ran; branch coverage asks whether each decision went both ways. A measured example, and why tools count differently.
What is the difference?
Line coverage asks whether each line ran. Branch coverage asks whether each decision went each way. A line can run while one of its branches never does, so 100% line coverage can hide untested paths. Track both. If you enforce one threshold, enforce branches.
A line that ran, a branch that did not
function label(user, fallback = 'Guest') {
return user ? user.name : fallback;
}
One test calls label({ name: 'Ada' }). The return line ran, so line
coverage is 100%. The function never returned fallback, and line coverage
cannot say so. Branch coverage reports one branch missing on that line.
We measured this with Jest 30.5.2 and its default Babel provider, on a file
with this function and one more: 5/5 lines, 6/7 branches. The missing branch
is fallback.
Statement and function coverage
- Statement coverage counts statements instead of lines. The two differ when one line holds several statements or one statement spans several lines.
- Function coverage asks whether each function was called at all.
Istanbul-based tools report all four. Many others report lines and branches.
Tools do not count branches the same way
The same three tests on the same JavaScript file:
| Tool | Lines | Branches |
|---|---|---|
| Jest, Babel provider | 5/5 | 6/7 |
| Jest, V8 provider | 14/17 | 4/5 |
node --test, built-in coverage | 14/17 | 5/6 |
All three agree on which branch is missing. They disagree on how many branches and lines exist. Other languages differ again:
- coverage.py counts a branch only when a line can jump to more than one next
line. The Python version of
label, on one line, reported 6/6 lines and 2/2 branches withfallbacknever returned. go test -coverreports statements and has no branch metric.- JaCoCo counts branches in bytecode, where each condition of an
&&is its own jump.
Compare a branch percentage with the same tool’s earlier number, not with another tool’s.
What branch coverage still misses
if (signedIn && !expired) {
Two tests can take this if both ways without ever trying a signed-in
session that has expired. Branch coverage is 100%.
MC/DC is the metric that catches it.
Neither metric says whether a test checked the result. A test can run every branch and assert nothing.
Measure them on your tests
npx supercov -- npm test
npx supercov runs latest gaps
Supercov runs your existing test command and reports line, branch and MC/DC coverage per test, for JavaScript, TypeScript, Python, Go, Java, Kotlin, Rust and Ruby. On the JavaScript file above it reported 7/9 lines, 4/6 branches and 1/3 MC/DC conditions. Assertion coverage then shows which executed statements your tests check.
Example evidence: fixture source, commands and recorded results (JSON). Measured October 12, 2026 with Supercov 4.3.0. These small examples are not a performance benchmark or a general conformance test.