Skip to main content
v2026.11,986 entries · CC-BY 4.0

coverage.py: Measure Python Test Coverage (Install, Run, Read Reports)

Install and run coverage.py, read missing-line and branch reports, set a CI threshold, and configure it in pyproject.toml, .coveragerc, setup.cfg or tox.ini.

Written and maintained by CASRAI Editorial Board

Published

On this page

coverage.py is a free Python tool that measures which lines (and, optionally, which branches) of your code are executed while your tests or scripts run, then reports the gaps. You install it with python3 -m pip install coverage, run your tests under it with coverage run -m pytest, and read the result with coverage report -m or coverage html. This guide follows the project’s official documentation (read 2026-11-13, documentation page showing version 7.17.3) and adds a configuration cross-walk, a worked reading of branch notation and a CI-gate recipe that most tutorials skip.

What coverage.py is used for in research

Research groups write analysis scripts, simulation code and data-processing pipelines in Python, and coverage.py answers one narrow question about them: which statements did the test suite never execute? Untested code paths are where silent errors hide, so the report points a reviewer or a new lab member at the functions that have no test behind them. It is a measurement of execution, not of correctness: 100% coverage means every measured line ran at least once, not that the results are right (editorial interpretation). It pairs naturally with practices described in our entries on computational reproducibility and FAIR4RS, where tested, documented software is part of making research code reusable.

The official home page lists support for Python 3.11 through 3.16.0a2 (including free-threaded builds) and PyPy3 3.11 for the release it documents. Check the current page before pinning an interpreter version for a long-running project.

Install or access (official source only)

The documentation’s install command is:

python3 -m pip install coverage

Documentation, release notes and source are linked from coverage.readthedocs.io. Use a virtual environment per project so the version you measure with is the version recorded for your analysis. There is no account or licence step.

Step-by-step: measure, report and view a test run

  1. Run your tests under coverage. The documented forms are coverage run -m pytest arg1 arg2, coverage run -m unittest discover and, for a plain script, coverage run something.py. This writes a data file named .coverage in the current directory.
  2. Limit measurement to your own code. Add --source=. (for example coverage run --source=. -m pytest). The docs note this restricts measurement to the current directory and also finds files that were never executed, which would otherwise be invisible in the report.
  3. Turn on branch measurement. Add --branch (coverage run --branch myprog.py) so the report also tracks jumps between lines, not just whether each line ran. The branch setting defaults to false.
  4. Print a text report with missing lines. coverage report -m (long form --show-missing) adds a Missing column listing line ranges such as 17-23 and, with branch measurement, jumps such as 40->45.
  5. Build an HTML report. coverage html writes htmlcov/index.html; open it in a browser to click through each file.
  6. Export for other tools. The docs list XML, LCOV and JSON output (coverage xml, coverage lcov, coverage json) alongside the annotate and debug subcommands. These are the formats coverage services and editors typically read; see our Codecov guide for the hosted-reporting side.

coverage run -m pytest versus pytest –cov

The pytest-cov plugin wraps coverage.py: after pip install pytest-cov, pytest --cov=myproj tests/ prints a table of statements, misses and cover percentage with a TOTAL row. Its documentation says it erases and combines coverage data files automatically, supports per-test contexts and works with pytest-xdist. The two entry points can import code differently, because python -m adds the current directory to sys.path; if a result differs between them, that is the first place to look. pytest-cov 7 dropped its .pth-file approach to subprocess measurement and recommends coverage.py’s own patch options instead.

Configuration: one setting, four file formats

Instead of repeating flags, put options in a config file. coverage.py looks in .coveragerc (the default), .coveragerc.toml, setup.cfg, tox.ini and pyproject.toml; override the choice with --rcfile=FILE or the COVERAGE_RCFILE environment variable. The section names differ by file, which is the most common source of “my setting is ignored” confusion; the data table below sets them side by side.

A documented pyproject.toml example:

[tool.coverage.run] branch = true source = ["src"] omit = ["*/tests/*"] parallel = true  [tool.coverage.report] fail_under = 90 show_missing = true

Key options from the configuration reference: branch and parallel (both under run, default false), source (when set, the ignore rules for measured files apply), omit (valid under run or report), fail_under and show_missing (under report; show_missing defaults to false).

Output and how to read it

The text report

Each row is a file with its statement count, the number missed, the cover percentage and, with -m, the Missing column. --skip-covered hides fully covered files so only files needing work remain. --sort accepts name, stmts, miss, branch, brpart and cover, and --format accepts text, markdown or total; --format=total prints only the overall percentage, handy for badges and scripts.

Reading branch notation: a worked example

The excerpt below is an illustrative composite written for this guide, not output from a real project; the notation follows the branch-coverage documentation.

Name        Stmts   Miss Branch BrPart  Cover   Missing ------------------------------------------------------- fit.py         20      3      8      2    80%   3-5, 4->2

In the Missing column, 3-5 means lines 3 to 5 never ran. 4->2 means the jump from line 4 back to line 2 never happened, for example a loop that was never iterated a second time. A file can have every line executed and still show partial branches. In the HTML report, lines with missing branches are shaded yellow and an annotation names the destination lines that were never reached. XML and JSON reports give statement and branch percentages separately.

Excluding code deliberately

A # pragma: no cover comment excludes that line from the report; placed on an if, def or class line it excludes the whole block. Excluded code still runs; it is only left out of the figures. By default coverage.py also excludes lines that contain only ..., if TYPE_CHECKING:, # pragma: no branch markers and branches known at compile time. To add your own patterns, use exclude_also (for example def __repr__ under [tool.coverage.report]); exclude_lines replaces the defaults, so the docs prefer exclude_also. Since version 7.6.0 exclusion regexes can span lines, using scoped flags such as (?s:...).

Failing a build under a threshold

coverage report --fail-under=MIN exits with status 2 if total coverage is below MIN, and the same threshold can be set as fail_under in configuration. In continuous integration, that exit status is what turns a coverage drop into a failed job. Treat the number as a team agreement, not a standard: the documentation defines the mechanism, not a recommended percentage.

Parallel runs and combining data

With coverage run --parallel-mode (or parallel = true), each process writes a distinct data file whose name includes the machine name, process id and a random number, for example .coverage.Geometer.pid8044.XJeYqEYx.H7cDrkGAlTVh. coverage combine merges them into one .coverage file. It also accepts explicit paths (coverage combine data1.dat windows_data_files/), --append to add to an existing file and --keep to retain the inputs, which are otherwise deleted.

Troubleshooting

  • A file is not being measured. Run with --debug=trace (or set COVERAGE_DEBUG=trace) to see what coverage.py decides about each file; COVERAGE_DEBUG_FILE=/tmp/cov.out sends that output to a file.
  • Line numbers look wrong or data seems stale. Source files changed after the data was collected. Run coverage erase and measure again.
  • def and class lines show as missed but their bodies are covered. Coverage started after the module’s definitions had already executed. When using the Python API, call coverage.start() before importing the modules you want measured.
  • Measurement is slow. Run with --debug=sys and check the core= line and whether the C tracer is available.

Free alternatives and neighbours

coverage.py itself is the measurement engine most Python tooling builds on. Related options, each from the documentation read for this guide:

Option Relationship to coverage.py Typical command
pytest-cov (MIT licence) pytest plugin that drives coverage.py; handles erase and combine, supports pytest-xdist pytest --cov=myproj tests/
unittest via coverage.py Standard-library test discovery measured directly coverage run -m unittest discover
Hosted reporting (Codecov) Service that reads coverage.py’s XML or LCOV output; see the Codecov guide Upload step in CI

FAQ

What does coverage.py measure?

By default, which statements executed during a run. With --branch it also records which jumps between lines occurred. It does not judge whether tests assert anything meaningful.

How do I run coverage.py with pytest?

Use coverage run -m pytest, then coverage report -m. Alternatively install pytest-cov and run pytest --cov=myproj tests/.

Where does coverage.py store its results?

In a .coverage data file in the current directory; parallel runs write suffixed files that coverage combine merges.

How do I make CI fail when coverage drops?

Set fail_under in configuration or pass --fail-under=MIN to coverage report; the command exits with status 2 below the threshold.

Which config file should I use?

Any supported file works; pyproject.toml suits projects that already use it. Mind the section names in the data table below.

Sources

Data: coverage.py configuration section names by config file

Compiled from the coverage.py configuration reference (read 2026-11-13): the section header each supported config file format uses for the run and report option groups, with the documented example form.

Period covered: 2026.

coverage.py configuration section names by config file
Config fileRun section headerReport section headerNotes
.coveragerc[run][report]Default config file name
pyproject.toml[tool.coverage.run][tool.coverage.report]TOML syntax: branch = true, source = ["src"]
setup.cfg[coverage:run][coverage:report]Prefixed section names
tox.ini[coverage:run][coverage:report]Prefixed section names, same as setup.cfg

Sources

Compiled by CASRAI from the sources above. Free to reuse under CC BY 4.0 with attribution.

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Ask CASRAI · free to try

Ask about coverage.py: Measure Python Test Coverage (Install, Run, Read Reports)

Ask your first 2 questions free below. Subscribers get 150 a day for $29 a month.

An AI assistant specialized in research administration. It cites the sources behind every answer, labels web answers and says when it can't answer.

Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.

Works on this site and inside Claude, Cursor and the AI tools you already use.

Everything CASRAI publishes — this page, the dictionary, the guides and the news — stays free to read, with no account and no card.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →