Skip to main content

rstest

CI codecov PyPI Python versions Wheel License Downloads Docs GitHub stars

Runs most pytest suites unchanged, in parallel, usually faster. Same fixtures, same plugins: byte-exact per-test outcomes at -n 0, and in parallel the same caveats as pytest-xdist (see Known gaps). Rust-orchestrated, parallel by default, with built-in suite diagnostics (--doctor) that tell you where your test time actually goes.

Note: This is the Python test runner on PyPI (pip install rstest). It is not related to the Rust fixture crate rstest on crates.io.

aiohttp, 4,469 tests:   pytest 193s  →  rstest 67s warm (150s cold), -n 8

Terminal recording: the aiohttp suite under pytest (193s), then rstest --doctor (67s, 14 parallel workers) pinpointing the wait-bound file that gates the suite

Same suite: pytest 193s → rstest 67s (warm, -n auto = 14 workers, as recorded); --doctor shows where the time goes. Current measured numbers: benchmarks.

📚 Full documentation → python-rstest.readthedocs.io

Quick start

Evaluating rstest? Run rstest try first: it runs your suite under plain pytest and under rstest, then tells you whether outcomes match and how much faster rstest was (details).

In any pytest project:

pip install rstest      # or: uv pip install rstest
rstest

Also works with your tool of choice:

uv add --dev rstest           # uv projects
poetry add --group dev rstest # Poetry
pdm add -dG test rstest       # PDM

Install rstest into the same environment as your tests: workers run in that interpreter. (A pipx / uv tool install also needs rstest in the project environment; see Installation.)

Requires Python 3.10+ on macOS, Linux, or Windows. Windows runs the full test gate in CI, but the 33-suite public corpus runs only on macOS/Linux, so Windows is validated at a smaller scale. rstest is alpha (0.x): expect breaking changes between minor versions until 1.0.

No config and no test changes needed: rstest runs your pytest suite in parallel (-n auto) out of the box. As with pytest-xdist, each worker is a separate process, so session- and module-scoped fixtures run once per worker, not once per run.

  • Tests that can't run in parallel (shared files, ports, databases) → @pytest.mark.serial (run exclusively, after the parallel phase).
  • Tests that depend on order within a file → --dist loadfile (keeps each file's tests on one worker); across files → rstest -n 0.
  • Want byte-exact pytest semantics → rstest -n 0 (single pytest session; per-test outcomes match pytest exactly).

Will rstest speed up your suite?

The wins come from suite shape, not magic. Quick self-check:

Your suite What to expect
Wait-bound (IO, sleeps, network, timeouts) Biggest win: xdist's default --dist load hands out consecutive batches in collection order with no timing data, so a file of slow tests clusters on a few workers and starts late; rstest's duration cache starts the slowest tests first, spread across workers.
CPU-bound, already splits well under xdist Parity, not a win: gain up to the performance-core count, same as xdist (sympy -n 8: 16.1s vs 14.8s, overlapping spreads).
Very many tests (100k+) Win over xdist: xdist's single Python controller becomes the bottleneck; rstest's orchestrator is Rust (pandas -n 8: 43s vs 89s).
Gated by one long test No win beyond that test: no worker count beats the long pole. --doctor names it.
Small (< ~10s serial) Little wall-time change: value is --watch, --changed, --doctor, not raw speed.
Many tiny per-service suites Speedup is per-suite; the aggregate CI win depends on your largest suites.

Two more things to know before you benchmark:

  • Warm cache matters. Duration-aware scheduling needs one run of timing data. First run is cold; the win arrives on run two. In ephemeral CI, persist .rstest_cache or expect cold-run timing.
  • Adopting rstest adopts pytest 9. rstest runs a vendored pytest 9.1.1 core. A suite that's warning-clean on recent pytest 8.x is almost always already pytest-9-clean; if not, clear deprecations first (the same upgrade you'd owe pytest anyway). rstest -n 0 surfaces them. Plugins run on that core too, so a plugin's pytest<9 pin is inert (details).

Fastest way to find out for real: rstest try runs your suite under plain pytest and under rstest -n auto, then reports whether outcomes match and how much faster rstest was. No migration, no config. (It runs the baseline as python -m pytest, so pytest must be installed in the project's environment for this one command.)

Benchmarks

Real open-source suites, end-to-end, with per-test outcome diffing against the pytest baseline: 100% parity on pandas, django-allauth and rich, and 99.91-99.98% on aiohttp, whose socket-leak warning flake hits xdist too (every known flake is catalogued in the docs).

Suite Tests pytest xdist -n 8 rstest -n 8
aiohttp 4,469 193s 160s 67s warm · 150s cold
pandas 193,843 190s 89s 43s (xdist's controller is the bottleneck)
django-allauth 2,050 26s 8.9s 5.8s
rich 981 3.7s 2.7s 2.5s

Apple M4 Max, CPython 3.13, pytest-xdist 3.8, median of 5 runs at the same -n for both runners. CPU-bound suites (sympy, scikit-learn) land at parity with xdist; see the benchmarks page.

Monorepo (langchain-ai/langgraph, 6 libs/* packages, 4,284 tests, each with its own pytest config; a single pytest can't run from the root at all):

wall parity
pytest: 6 serial invocations 880.4s baseline
rstest at the root, cold 245.7s (3.6×) 100%

Only the cold run is measured. From its per-project duration caches, a warm run is projected (not measured) at 121–133s (6.6–7.3×); discount that until you measure your own.

Full methodology: benchmarks.

Why rstest

pytest pytest-xdist rstest
Runs your suite unchanged ✅ parallel-safe tests ✅ at -n 0; parallel-safe tests at -n ≥ 2
Parallel by default ❌ ⚙️ opt-in ✅
Duration-aware scheduling plugin (pytest-split, across CI jobs) ❌ ✅
Crashed workers replaced mid-run ❌ ✅ ✅
Crash pinned to the exact test ❌ inferred from queue ✅ explicit per-test signal
Suite diagnostics ❌ ❌ ✅ --doctor
Watch mode plugin (pytest-watch) --looponfail (deprecated) ✅ built-in
  • pytest underneath. Forwards the pytest flag surface; runs conftest, fixtures, parametrize, marks, and pytest plugins (pytest-django, pytest-asyncio, hypothesis, …) through a vendored pytest core.
  • Parallel by design. Duration-aware work distribution that starts the slowest tests first (per test, or per file when a large suite gets lazy collection); @pytest.mark.serial and --dist loadfile safety rails; crashed workers respawn without losing your run.
  • rstest --doctor. Wait-bound tests, parallel-floor analysis, fixture hotspots, slowest files.
  • rstest --watch. Instant reruns on save; changed test files rerun alone, source changes rerun only the tests the import graph says are affected.
Compatibility contract

At -n 0 (byte-exact mode), per-test outcomes match pytest exactly: one vendored-pytest session; any difference at -n 0 is a bug. The guarantee is per-test outcomes (every phase, skips, xfails). With no --output set, the terminal output at -n 0 is pytest's own as well, with rstest's extras (doctor, coverage, gate messages) appended after it, and --junitxml is pytest's own document at every worker count. In parallel modes, outcomes are preserved for parallel-safe tests; tests with hidden time/ordering/shared-state assumptions can flake under high concurrency, exactly as under pytest-xdist. rstest --doctor and lower -n values help find and contain them; @pytest.mark.serial is the escape hatch.

Vendored pytest 9. rstest runs a vendored pytest 9.1.1 core, so adopting rstest adopts pytest 9's behavior regardless of the pytest version installed. The 8→9 gap is a cleanup major (removes already-deprecated APIs); a suite warning-clean on recent pytest 8.x is almost always pytest-9-clean. If it isn't, clear the deprecations first: the same upgrade you'd owe pytest anyway. There is one vendored core, tracked forward; no older-core build.

Silent-at--n ≥ 2 plugins. A few plugins that need a single controller process are no-ops in parallel: report plugins such as pytest-reportlog and pytest-json-report write nothing (no crash), and terminal-UI plugins (pytest-sugar and friends) don't paint; data-level behavior is unaffected. --html is handled by rstest itself and works at any worker count. Full list: Known gaps.

rstest --doctor

Runs your suite, then answers where does the time actually go?, from data the runner already owns (per-test wall/CPU time, per-fixture setup):

================== rstest doctor ==================
4442 tests, 185.8s test time (wall 67.7s, 8 workers)

WAIT-BOUND: 95% of test time (176.5s) is waiting, not computing (sleeps / IO / timeouts).
    54.20s waiting of   54.25s  tests/test_proxy_functional.py::test_proxy_https_multi_conn_limit
    10.97s waiting of   10.97s  tests/test_proxy_functional.py::test_proxy_https_connect
  ... and 33 more

PARALLEL FLOOR: the longest test (54.2s) exceeds the ideal per-worker share (23.2s at -n 8);
no worker count can finish faster than its longest test. Gate tests:
    54.25s  tests/test_proxy_functional.py::test_proxy_https_multi_conn_limit

FIXTURE HOTSPOTS (setup time across all workers):
     0.79s   4442x  scope=function blockbuster
     0.54s    157x  scope=function transport

SLOWEST FILES:
   150.46s (81.0%)  tests/test_proxy_functional.py
     8.60s ( 4.6%)  tests/test_client_functional.py
===================================================

That's aiohttp's real suite: one file is 81% of total test time, almost all of it waiting on 10-second proxy timeouts. (Doctor counts tests with a recorded call duration, so skipped tests drop out: 4442 here against the 4,469 collected.) More →

Docs

License

Licensed under either of

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Metadata

Release files for rstest 0.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for rstest 0.8.0
File
rstest-0.8.0-py3-none-win_arm64.whl Python 3 none Windows ARM64 Details
rstest-0.8.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
rstest-0.8.0-py3-none-musllinux_1_2_x86_64.whl Python 3 none Linux musl 1.2+ x86-64 Details
rstest-0.8.0-py3-none-musllinux_1_2_aarch64.whl Python 3 none Linux musl 1.2+ ARM64 Details
rstest-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details
rstest-0.8.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details
rstest-0.8.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details

Total release size: 31.4 MB

Release files / rstest-0.8.0-py3-none-win_arm64.whl

Download URL rstest-0.8.0-py3-none-win_arm64.whl
Size 3.9 MB
Tags Python 3 Windows ARM64
SHA-256 checksum
How to use checksums
73f95c6f78207db06978013757956f63c51d5d89462b0692a3ba48b79811c815
BLAKE2b-256 checksum
How to use checksums
3e38195503fe64f55e9011bbb9f4e7562de27eadf61206e52e9025c76aa09771
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release files / rstest-0.8.0-py3-none-win_amd64.whl

Download URL rstest-0.8.0-py3-none-win_amd64.whl
Size 4.2 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
36d4dc12e412ea13c0b69d15f8b991493fb8caeb5da70b28e6fa9884ac3e9e4a
BLAKE2b-256 checksum
How to use checksums
921a0e7b9c7aebda3326a7ea9c1571a975faee3499039c7e108d5ea2a993eaec
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release files / rstest-0.8.0-py3-none-musllinux_1_2_x86_64.whl

Download URL rstest-0.8.0-py3-none-musllinux_1_2_x86_64.whl
Size 4.9 MB
Tags Linux musl 1.2+ x86-64 Python 3
SHA-256 checksum
How to use checksums
e9562f571cf798226e908ea1ca3f8287a404a232b04428b08a9011925b15f5cc
BLAKE2b-256 checksum
How to use checksums
c40478ae5213e500be85488bbaddf994f1da87332c77621b828851146a65e453
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release files / rstest-0.8.0-py3-none-musllinux_1_2_aarch64.whl

Download URL rstest-0.8.0-py3-none-musllinux_1_2_aarch64.whl
Size 4.7 MB
Tags Linux musl 1.2+ ARM64 Python 3
SHA-256 checksum
How to use checksums
39f677d47d5785878a485d664a831b2dbd9df05909d6d02296cc6fffd305d4b0
BLAKE2b-256 checksum
How to use checksums
049ab7209d3c3a00fe92081354e67474b5fdbc255f353933294cef7012a36fe5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release files / rstest-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL rstest-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 4.7 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
23a2ececd4578d4ff7bcec1a888ccbd89b0cffb749c3a8b29b9251d65d713e25
BLAKE2b-256 checksum
How to use checksums
1851edfd2f3f5d6476abbbcbe64ce7b5d27df643d73999cc16a386c8f2f21752
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release files / rstest-0.8.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL rstest-0.8.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 4.7 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
eec823571dc970e9201afc22820613623d8eb204a2d292d0a7c1b9d7db246a37
BLAKE2b-256 checksum
How to use checksums
23ef73a6591e6a709f67fcb8cd29e62ed98c9b62851081fc83f92a71ffeac0ac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release files / rstest-0.8.0-py3-none-macosx_11_0_arm64.whl

Download URL rstest-0.8.0-py3-none-macosx_11_0_arm64.whl
Size 4.3 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
cf3666418d9d8e63590379f3ebcffde7d9f0d2b53d75406d33aad5c1d90808f8
BLAKE2b-256 checksum
How to use checksums
d25790950bad030f924ae17ac108f5171802c97c3491ba9676c3421d09136268
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release history Release notifications | RSS feed

0.9.0

7 release files

This release

0.8.0 This release

7 release files

0.7.0

7 release files

0.6.1

7 release files

0.5.0

7 release files

0.4.0

7 release files

0.3.1

7 release files

0.2.1

7 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page