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

# 0. Change the version in pyproject.toml
# 1. build
uv run python -m build

# 2. upload
uv run twine upload --skip-existing 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.4.tar.gz (58.7 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.4-py3-none-any.whl (67.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: coverage_stats-0.1.4.tar.gz
  • Upload date:
  • Size: 58.7 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.4.tar.gz
Algorithm Hash digest
SHA256 8ff56e1dd131642c42077d144888dbfc3946ca6e310a7dd8bb0c37859784d402
MD5 0bdf08f31cdf7e47926664ac08169712
BLAKE2b-256 4f62fbaf594bdf5b945484da06b6fa634ed0768f0281f93717d362a7d66821c3

See more details on using hashes here.

File details

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

File metadata

  • Download URL: coverage_stats-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 67.3 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.4-py3-none-any.whl
Algorithm Hash digest
SHA256 4c356943ebcb08b3791f602f2d47243e6396317e6a130c6b58cde238348d3029
MD5 e7cbc66cf915daf8e16b4fdb331b9de6
BLAKE2b-256 acb5deb2be3bd0f7b5e1c570cf4650103c39bb18198cf1b3713dba38e8dab960

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