# How to Measure Code Coverage with pytest-cov


[pytest-cov](https://pytest-cov.readthedocs.io/) integrates [coverage.py](https://coverage.readthedocs.io/) into [pytest](https://pydevtools.com/handbook/reference/pytest.md) 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](https://pydevtools.com/handbook/reference/uv.md):

```bash
uv add --dev pytest-cov
```

## Run tests with coverage

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

```bash
uv run pytest --cov=src/mypackage
```

pytest-cov appends a coverage summary to the test output:

```console
$ 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:

```bash
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](https://pydevtools.com/handbook/reference/pyproject.toml.md) so coverage runs automatically:

```toml {filename="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:

```toml {filename="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:

```toml {filename="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:

```bash
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:

```bash
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:

```text {filename=".gitignore"}
htmlcov/
```

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

```bash
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:

```yaml {filename=".github/workflows/test.yml"}
name: Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v8.1.0
      - 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

- [How to run tests using uv](https://pydevtools.com/handbook/how-to/how-to-run-tests-using-uv.md) covers filtering, output control, and persistent pytest defaults
- [How to run tests in parallel with pytest-xdist](https://pydevtools.com/handbook/how-to/how-to-run-tests-in-parallel-with-pytest-xdist.md) distributes tests across CPU cores (works alongside pytest-cov)
- [Essential pytest plugins](https://pydevtools.com/handbook/explanation/essential-pytest-plugins.md) covers four plugins that complement coverage measurement
- [pytest-cov documentation](https://pytest-cov.readthedocs.io/) for the full list of CLI options and report types
- [coverage.py configuration reference](https://coverage.readthedocs.io/en/latest/config.html) for every `[tool.coverage.*]` setting
