Code coverage · Comparison
lcov vs Cobertura: coverage report formats
lcov is text and names each branch; Cobertura is XML with a branch count per line. The same run in both formats, and which tools read which.
Which format should you produce?
Produce the one the tool reading it accepts; most coverage tools can write both. lcov is plain text with one record per line, and it names each branch. Cobertura is XML, and gives a branch count per line. When both are accepted, lcov keeps more detail and is easier to merge.
The same run in both formats
One Jest run on one file, written with both reporters. From the lcov file:
SF:src/access.js
FN:8,label
FNDA:1,label
DA:9,1
LF:5
LH:5
BRDA:9,3,0,1
BRDA:9,3,1,0
BRF:7
BRH:6
end_of_record
From the Cobertura file:
<class name="access.js" filename="src/access.js" line-rate="1" branch-rate="0.8571">
<lines>
<line number="9" hits="1" branch="true" condition-coverage="50% (1/2)"/>
</lines>
</class>
Both say line 9 ran once and half its branches were taken. Only lcov says
which half: BRDA:9,3,1,0 is the second branch, taken 0 times.
How they differ
| lcov | Cobertura | |
|---|---|---|
| Syntax | Text, one record per line | XML |
| Lines | DA:line,hits | <line number hits> |
| Branches | BRDA, one record per branch | condition-coverage, a count per line |
| Functions | FN and FNDA | <method> |
| Totals | LF, LH, BRF, BRH per file | line-rate and branch-rate at every level |
| File paths | SF: per file | filename, relative to a <source> |
| MC/DC | MCDC, with MCF and MCH per file, from lcov 2.0 | None |
| Merging | lcov --add-tracefile | Needs a tool that understands the XML |
Neither format records which test ran a line. Only lcov 2 has a record for
MC/DC, and only readers that ask for it (--mcdc-coverage) read it.
Who reads which
Check the tool that consumes the report:
- Codecov accepts both, and JaCoCo XML. So does Coveralls; see Codecov vs Coveralls.
- GitLab’s coverage view in merge requests reads Cobertura and JaCoCo XML, not lcov.
- SonarQube reads lcov for JavaScript and TypeScript, and Cobertura-style XML from coverage.py for Python.
Paths are the usual failure
A report that uploads but shows no coverage usually has paths the reader
cannot match to the repository. In the Jest run above, <source> held the
absolute path of the project on the machine that ran the tests. Generate
reports from the repository root, and prefer repository-relative paths.
Cobertura is also the name of a Java coverage tool, last released in 2015. The format outlived it. See JaCoCo vs Cobertura.
Export both from Supercov
npx supercov runs report --format lcov --output coverage/lcov.info
npx supercov runs report --format cobertura --output coverage/cobertura.xml
Supercov writes both from one run, with repository-relative paths and a stable order. A line’s hit count is 1 or 0, because Supercov records that a line ran, not how often. Per-test coverage and MC/DC stay in Supercov’s own report, since neither format can hold them.
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.