Skip to content

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-cov

Run tests with coverage

Pass --cov with the path to your source code:

uv run pytest --cov=src/mypackage

pytest-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-missing

The 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:

pyproject.toml
[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:

pyproject.toml
[tool.coverage.run]
source = ["src/mypackage"]
branch = true

[tool.coverage.report]
show_missing = true

Setting 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:

pyproject.toml
[tool.coverage.report]
fail_under = 80

When 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=80

Generate 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=html

This 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:

.gitignore
htmlcov/

To generate both the terminal summary and an HTML report in a single run:

uv run pytest --cov-report=term-missing --cov-report=html

Add coverage to GitHub Actions

A workflow step that runs pytest-cov enforces coverage on every push:

.github/workflows/test.yml
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-missing

If fail_under is set in pyproject.toml, the step fails when coverage drops below the threshold. No extra CI configuration is needed.

Learn More

Last updated on