How to Measure Code Coverage with pytest-cov
pytest-cov integrates coverage.py into pytest runs. Instead of running coverage as a separate command, you get line-by-line coverage data in the same terminal output as your test results.
Install pytest-cov
Add it as a dev dependency with uv:
uv add --dev pytest-covRun tests with coverage
Pass --cov with the path to your source code:
uv run pytest --cov=src/mypackagepytest-cov appends a coverage summary to the test output:
$ uv run pytest --cov=src/mypackage
========== tests coverage ==========
Name Stmts Miss Cover
--------------------------------------------------
src/mypackage/__init__.py 2 0 100%
src/mypackage/core.py 45 12 73%
--------------------------------------------------
TOTAL 47 12 74%
Add --cov-report=term-missing to see which lines lack coverage:
uv run pytest --cov=src/mypackage --cov-report=term-missingThe Missing column shows the exact line numbers your tests did not execute.
Configure coverage in pyproject.toml
Typing --cov flags on every run gets tedious. Move them into pyproject.toml so coverage runs automatically:
[tool.pytest.ini_options]
addopts = "--cov=src/mypackage --cov-report=term-missing"coverage.py reads its own settings from [tool.coverage.*] sections in the same file:
[tool.coverage.run]
source = ["src/mypackage"]
branch = true
[tool.coverage.report]
show_missing = trueSetting branch = true measures whether both sides of every if/else branch execute, not just whether each line runs. Branch coverage catches dead branches that statement coverage misses.
Enforce a coverage threshold
Add fail_under to the report configuration:
[tool.coverage.report]
fail_under = 80When total coverage drops below 80%, pytest exits with a nonzero status code. CI pipelines that check the exit code will fail the build automatically.
The same option is available as a CLI flag:
uv run pytest --cov-fail-under=80Generate an HTML report
The terminal summary shows which lines are missing, but an HTML report lets you click through each file and see uncovered lines highlighted in red:
uv run pytest --cov-report=htmlThis creates an htmlcov/ directory. Open htmlcov/index.html in a browser to browse the results.
Add htmlcov/ to .gitignore so generated reports stay out of version control:
htmlcov/To generate both the terminal summary and an HTML report in a single run:
uv run pytest --cov-report=term-missing --cov-report=htmlAdd coverage to GitHub Actions
A workflow step that runs pytest-cov enforces coverage on every push:
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/[email protected]
- run: uv run pytest --cov --cov-report=term-missingIf fail_under is set in pyproject.toml, the step fails when coverage drops below the threshold. No extra CI configuration is needed.
Learn More
- How to run tests using uv covers filtering, output control, and persistent pytest defaults
- How to run tests in parallel with pytest-xdist distributes tests across CPU cores (works alongside pytest-cov)
- Essential pytest plugins covers four plugins that complement coverage measurement
- pytest-cov documentation for the full list of CLI options and report types
- coverage.py configuration reference for every
[tool.coverage.*]setting