Python · Comparison
pytest-cov vs coverage.py
pytest-cov is a pytest plugin that runs coverage.py, so the numbers match. The real difference is pytest-xdist: plain coverage run reported 0% of lines.
pytest-cov or coverage.py: which should you run?
They do not compete: pytest-cov is a pytest plugin that runs coverage.py for you, so the numbers are the same. Use pytest-cov when you run tests in parallel with pytest-xdist, or want coverage as a pytest flag. Run coverage.py directly when you want one dependency fewer, or your runner is not pytest.
The two commands
pip install coverage
coverage run --branch --source=src -m pytest
coverage report -mpip install pytest-cov
pytest --cov=src --cov-branch --cov-report=term-missing
Both read the same configuration: [tool.coverage] tables in pyproject.toml,
or .coveragerc. Exclusions, fail_under and contexts apply either way.
Same numbers, one real difference
One module and three tests, on coverage.py 7.16.2, pytest-cov 7.1.0, pytest 9.1.1 and Python 3.13.1:
| Command | Lines | Branches |
|---|---|---|
coverage run -m pytest | 6/6 | 2/2 |
pytest --cov | 6/6 | 2/2 |
coverage run -m pytest -n 2 | 0/6 | none |
pytest -n 2 --cov | 6/6 | 2/2 |
With -n 2, pytest-xdist runs the tests in worker processes. Plain
coverage run measured only the process that started them and warned “No data
was collected”. pytest-cov starts coverage in every worker and combines the
results.
coverage.py can do it alone with two settings and one more command. This also gave 6/6:
[run]
parallel = True
patch = subprocesscoverage run -m pytest -n 2
coverage combine
coverage reportWhat pytest-cov adds, and what it dropped
Its documentation
lists four things over coverage run -m pytest: xdist support, combining and
reporting in one step, --cov-context=test to record which test ran each
line, and the same sys.path as a plain pytest run.
Since 7.0.0 it no
longer measures subprocesses your tests start. Use coverage.py’s
patch = subprocess for that. It needs coverage.py 7.10.6 or newer.
What neither shows
Both report 100% on this module. Two things were never tested:
if signed_in and not expiredran both ways, but no test has a signed-in session that has expired.return user["name"] if user else fallbacknever returnedfallback. coverage.py measures a branch as a line that can jump to more than one next line, and this line has one.
What Supercov adds
npx supercov -- pytest
npx supercov runs latest gaps
Supercov runs your own pytest command in your virtual environment, xdist workers included. On the same module it reported 6/6 lines, 5/6 branches and 1/3 MC/DC conditions, and named both gaps. It records coverage per test.
For other Python tools, see Python coverage: coverage.py, SlipCover, and Supercov.
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.