# 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. Web page: https://supercov.com/compare/pytest-cov-vs-coverage · Published 2026-10-12 All Supercov pages for agents: https://supercov.com/llms.txt · Full text: https://supercov.com/llms-full.txt ## 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 ```sh pip install coverage coverage run --branch --source=src -m pytest coverage report -m ``` ```sh pip 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: ```ini [run] parallel = True patch = subprocess ``` ```sh coverage run -m pytest -n 2 coverage combine coverage report ``` ## What pytest-cov adds, and what it dropped Its [documentation](https://pytest-cov.readthedocs.io/en/latest/readme.html) 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](https://pytest-cov.readthedocs.io/en/latest/changelog.html) 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 expired` ran both ways, but no test has a signed-in session that has expired. - `return user["name"] if user else fallback` never returned `fallback`. coverage.py [measures a branch](https://coverage.readthedocs.io/en/7.16.2/branch.html) as a line that can jump to more than one next line, and this line has one. ## What Supercov adds ```sh 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](https://supercov.com/compare/python-code-coverage.md). Example evidence: [fixture source, commands and recorded results (JSON)](https://supercov.com/downloads/tool-comparisons-2026-10-12.json). Measured October 12, 2026 with Supercov 4.3.0. These small examples are not a performance benchmark or a general conformance test.