Coverage¶
pytest-cov works under rstest, including in parallel mode:
$ rstest -n auto --cov=mypkg --cov-report=term-missing
...
4 passed in 0.20s
Name Stmts Miss Cover Missing
-------------------------------------------------
mypkg/__init__.py 6 1 83% 10
-------------------------------------------------
TOTAL 6 1 83%
Coverage percentages match a serial pytest run exactly — the data is the same; only the collection is parallel.
How it works¶
Each worker runs pytest-cov in its distributed-worker mode (the same mode
it uses under pytest-xdist): coverage is measured per worker and saved as
suffixed .coverage.* data files. After the run, rstest plays the role
xdist's master session would: it combines the data files and renders your
requested reports.
Supported pytest-cov options:
| Option | Behavior |
|---|---|
--cov=PKG (repeatable) |
measured in every worker |
--cov-report=term / term-missing |
printed after the summary |
--cov-report=xml[:path] / html[:dir] / json / lcov / annotate |
written by the orchestrator |
--cov-fail-under=N |
enforced after combining; run exits 1 below N |
--cov-context=test |
per-test line contexts, preserved through the parallel merge (see below) |
.coveragerc / [tool.coverage.*] config |
honored (read by coverage itself) |
Multiple --cov-report values compose, as under pytest-cov.
Per-test contexts (--cov-context=test)¶
--cov-context=test records which test covered each line. Under rstest the
contexts survive the parallel merge: each worker records into its own data
file and the combine keeps the labels, so a line executed by tests on different
workers ends up attributed to each of them — identical to a serial run, at
parallel speed. (--cov-report=html/json are rendered with show_contexts
so the per-test attribution shows up in the report.)
A --cov-context=test run also writes a line→test index to
.rstest_cache/coverage_index.json — the map
--changed uses to select only the tests
whose coverage actually executed the changed lines. Warm it by running your
coverage suite once with --cov-context=test; persist .rstest_cache across
CI runs the same way you persist it for scheduling.
Notes¶
- At
-n 0pytest-cov runs in its ordinary central mode and produces its own report through the vendored pytest session — rstest does not re-render it, so the byte-exact contract holds. (In parallel mode rstest combines the per-worker data and renders the report, as xdist's master would.) - With
--shard, each shard measures only the tests it ran. For a suite-wide number: on each shard skip rendering (--cov-report=), then rename its data file uniquely before uploading — every shard writes a file named.coverage, so they collide on a shared artifact. Give each a distinct suffix (coverage treats.coverage.<anything>as a combinable data file):
$ rstest -n auto --shard $K/$N --cov=mypkg --cov-report=
$ mv .coverage .coverage.shard-$K # unique per shard before upload
In a final merge job, download all .coverage.shard-* files, then
coverage combine && coverage report. --cov-fail-under is per-shard —
enforce the global threshold in that merge step (coverage report
--fail-under=N), not on individual shards.
- Branch coverage (--cov-branch) forwards like any other flag. Per-test
contexts (--cov-context=test) are preserved through the merge and drive the
--changed index — see Per-test contexts.
- Worker data files live in the invocation directory during the run and
are combined into .coverage at the end — the same lifecycle as xdist.