Skip to main content

Per-line deliberate/incidental coverage, # tests, # asserts & other stats stats for pytest

Project description

coverage-stats

A pytest plugin that tracks deliberate vs incidental line coverage per test. It works like a pytest plugin, that reports (in a way similar to how coverage.py does it):

  1. The number of asserts that were executed in tests that covered each of the lines reported
  2. The number of times each line was executed
  3. It distinguishes between incidental coverage and deliberate coverage. Deliberate coverage means that a test was marked with @covers(...) so we can know exactly what lines in the app were tested on purpose in that test. Incidental coverage means the line was covered in tests, but not deliberately

Install

pip install coverage-stats

Usage

pytest --coverage-stats

# then open in the browser ./coverage-stats-report/index.html

Mark which lines a test deliberately covers using the covers decorator:

from coverage_stats import covers

@covers("mymodule.MyClass.my_method")
def test_my_method():
    ...

HTML Report

Generate a self-contained HTML report:

pytest --coverage-stats --coverage-stats-format=html

The report is written to coverage-stats-report/ by default. To change the output directory:

pytest --coverage-stats --coverage-stats-format=html --coverage-stats-output=reports/

The report includes a folder-collapsible index with per-file summary metrics, and a per-file page showing line-level deliberate vs incidental execution counts and assert density.

Index page columns

Each row represents one file or folder. Columns are toggleable via checkboxes above the table; the default visibility is noted below.

Column Default Description
Stmts visible Total number of statements + branches tracked in the file or folder.
Total % visible Percentage of statements + branches covered by any test (deliberate or incidental). Files with nothing to cover (e.g. empty __init__.py) show 100%.
Deliberate % visible Percentage of statements + branches covered by at least one test that explicitly declares coverage via @covers(...).
Incidental % visible Percentage of statements + branches covered incidentally — executed by tests, but not via a @covers declaration.
Del. Covered hidden Raw count of statements + branches covered deliberately. Colored using the same level as the Deliberate % column.
Inc. Covered hidden Raw count of statements + branches covered incidentally. Colored using the same level as the Incidental % column.
Inc. Asserts hidden Total number of assert statements executed during incidental coverage of this file or folder.
Del. Asserts hidden Total number of assert statements executed during deliberate coverage of this file or folder.
Inc. Assert Density hidden Incidental assert count divided by total statements + branches. A higher value means more assertions are observing each line incidentally.
Del. Assert Density hidden Deliberate assert count divided by total statements + branches. A higher value means more targeted assertions are exercising each line.

Percentage columns are colored on a 10-level red → green scale (0–9%, 10–19%, …, 90–100%). Assert count and density columns are colored relative to the maximum value in the current report (divided into up to 10 equal buckets).

File report columns

Each row represents one source line. Columns are toggleable via checkboxes above the table.

Column Default Description
Inc. Executions visible Number of times the line was executed by incidental tests.
Del. Executions visible Number of times the line was executed by deliberate tests (tests with a matching @covers declaration).
Inc. Asserts visible Number of assert statements executed in all of the tests that ran when the line was executed incidentally.
Del. Asserts visible Number of assert statements executed in all of the tests that ran when the line was executed incidentally.
Inc. Tests visible Number of distinct incidental tests that executed this line.
Del. Tests visible Number of distinct deliberate tests that executed this line.

Row background colors: green = covered deliberately, yellow = covered only incidentally, orange = partially covered (some branches missed), red = not covered at all.

Scoping profiling to specific directories

By default all non-stdlib, non-site-packages files are profiled. To limit profiling to specific source directories, set coverage_stats_source in pyproject.toml:

[tool.pytest.ini_options]
coverage_stats_source = "src"

Multiple directories are space-separated:

[tool.pytest.ini_options]
coverage_stats_source = "src/mypackage src/otherpackage"

Other output formats

JSON and CSV outputs are also supported:

pytest --coverage-stats --coverage-stats-format=json,csv --coverage-stats-output=reports/

JSON (coverage-stats.json) — machine-readable, suitable for CI dashboards and trend analysis.

CSV (coverage-stats.csv) — one row per line, columns: file, lineno, incidental_executions, deliberate_executions, incidental_asserts, deliberate_asserts.

Note: Assert density metrics require pytest's default assertion rewriting. Running with --assert=plain disables assert counting.

Development

Running the test suite

Install nox (once):

uv tool install nox

And then here are examples of how you could run the tests, linting and type checking

# tests (all Python versions), mypy, ruff
nox

# only the tests (all Python versions)
nox -s tests

# Only the tests, python 3.12
nox -s "tests-3.12"

Individual sessions:

Session What it runs
tests pytest across Python 3.9–3.14
mypy mypy strict type-checking
lint ruff

Pre-commit hook

A pre-commit hook runs the full nox suite before every commit and blocks it if any check fails. To enable it, first install nox and pre-commit (once):

uv tool install nox
uv tool install pre-commit

Then install the hook into your local clone (once):

pre-commit install

From that point on, every git commit automatically runs all nox sessions. To skip the hook for a single commit (e.g. a work-in-progress), use git commit --no-verify.

Tip: The full matrix (Python 3.9–3.14 + mypy + lint) can be slow locally. To run just one Python version instead, edit the entry line in .pre-commit-config.yaml to nox -s "tests-3.12" mypy lint.

Type checking

Run mypy directly (without nox):

pip install -e ".[dev]"
mypy src/

mypy is configured in pyproject.toml under [tool.mypy] with strict mode enabled.

Publishing

# 1. build
uv run python -m build

# 2. upload
uv run twine upload dist/*

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

coverage_stats-0.1.2.tar.gz (47.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

coverage_stats-0.1.2-py3-none-any.whl (55.8 kB view details)

Uploaded Python 3

File details

Details for the file coverage_stats-0.1.2.tar.gz.

File metadata

  • Download URL: coverage_stats-0.1.2.tar.gz
  • Upload date:
  • Size: 47.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for coverage_stats-0.1.2.tar.gz
Algorithm Hash digest
SHA256 87c45d31c664a6991601dfa8c0fe028b08454864ffefbda95c1f1767baab7db0
MD5 8a660c7e0b1240e33e7f2e8ea4ec266d
BLAKE2b-256 c2dd2a1f414e54833b5e5f739bbda5fbe35f9af3fa4005582086a1fa6e7dd570

See more details on using hashes here.

File details

Details for the file coverage_stats-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: coverage_stats-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 55.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for coverage_stats-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d9e565ca287f713bd73a4622d5bad1fa865a820c2277bdc37d8774a8ef2549f7
MD5 3223b83b3ffdbd6ec602a286ea0e2dd6
BLAKE2b-256 701f37183b2385050e7cfc16db17d0080949e7c58a1d5fc738cbc6c7db3af596

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page