rstest
Run your existing pytest suite in parallel, unchanged. Same fixtures,
same plugins, same outcomes — just faster. Rust-orchestrated, parallel by
default, with built-in suite diagnostics (--doctor) that tell you where
your test time actually goes.
aiohttp, 4,469 tests: pytest 197s → rstest 68s warm (151s cold)
📚 Full documentation → python-rstest.readthedocs.io
Quick start
In any pytest project:
pip install rstest # or: uv pip install rstest
rstest
Also works with your tool of choice:
uv add rstest # uv projects
poetry add rstest # Poetry
pdm add rstest # PDM
pipx install rstest # isolated CLI install
No config. No test changes. If pytest runs it, rstest runs it — in
parallel (-n auto), out of the box.
- Tests that can't run in parallel →
@pytest.mark.serial(run exclusively, after the parallel phase). - Order-dependent suites →
--dist loadfile. - Want byte-exact pytest semantics →
rstest -n 0(single pytest session).
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 — test-granular dispatch splits the slow files xdist pins to one worker. |
| CPU-bound, already splits well under xdist | Parity, not a win — gain up to core count, same as xdist (pandas: 63s vs 61s). |
| 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 truths worth knowing 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_cacheor 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 0surfaces them.
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.
Benchmarks
Real open-source suites, end-to-end, with per-test outcome diffing against the pytest baseline — 100% parity on the measured run (a few tests flake under plain pytest itself; those are catalogued in the docs).
| Suite | Tests | pytest | xdist (-n 8) |
rstest |
|---|---|---|---|---|
| aiohttp | 4,469 | 197s | 160s | 68s warm · 151s cold |
| pandas | 193,627 | 182s | 61s | 63s (parity, not a win) |
| django-allauth | 2,050 | 22s | 8s | 8s (-n 4) |
| rich | 981 | 3.4s | 2.8s | 2.5s (-n 4) |
Apple Silicon, CPython 3.13, pytest-xdist 3.8.
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% (measured) |
| rstest at the root, warm | 121–133s (6.6–7.3×) | projected |
The cold run is measured. The warm figure is a projection, not a recorded run — the cold run's per-package duration caches predict where the planner lands once warm, so it carries no parity number. Discount it until you measure your own.
Full methodology: benchmarks.
Why rstest
| pytest | pytest-xdist | rstest | |
|---|---|---|---|
| Runs your suite unchanged | ✅ | mostly | ✅ |
| Parallel by default | ❌ | ⚙️ opt-in | ✅ |
| Duration-aware scheduling | ❌ | ❌ | ✅ |
| Crashed workers respawn mid-run | ❌ | ❌ | ✅ |
| Suite diagnostics | ❌ | ❌ | ✅ --doctor |
| Watch mode | plugin | ❌ | ✅ built-in |
- Drop-in. 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. Test-granular work distribution with duration-aware
scheduling;
@pytest.mark.serialand--dist loadfilesafety 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, outcomes are byte-exact — one vendored-pytest session; any
difference at -n 0 is a bug. 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 master
process are no-ops in parallel: --html (pytest-html) writes nothing (no
crash) — generate reports at -n 0/-n 1. Terminal-UI plugins (pytest-sugar
and friends) don't paint; data-level behavior is unaffected. 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
... 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.
FIXTURE HOTSPOTS (setup time across all workers):
0.79s 4442x scope=function blockbuster
SLOWEST FILES:
150.46s (81.0%) tests/test_proxy_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. More →
Docs
- Getting started
- Migrating from pytest
- Migrating from pytest-xdist
- Parallel safety
- Suite diagnostics (
--doctor) - Watch mode
- CI quickstart
- CLI reference
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
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.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rstest-0.5.0-py3-none-win_arm64.whl.
File metadata
- Download URL: rstest-0.5.0-py3-none-win_arm64.whl
- Upload date:
- Size: 2.5 MB
- Tags: Python 3, Windows ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3e2df09ac3bcd90e309bc1d7e18bd6291064f7e1b59cb9a5d8bc51410e40c94c
|
|
| MD5 |
62b98ce515209d3cc663affee7d1da6b
|
|
| BLAKE2b-256 |
084e8dfae95c0fe7008c6ec5d48f9f4dc60ff04fab502c67d06b51828ba8f6d9
|
Provenance
The following attestation bundles were made for rstest-0.5.0-py3-none-win_arm64.whl:
Publisher:
release.yml on KovantAI/rstest
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rstest-0.5.0-py3-none-win_arm64.whl -
Subject digest:
3e2df09ac3bcd90e309bc1d7e18bd6291064f7e1b59cb9a5d8bc51410e40c94c - Sigstore transparency entry: 2742395189
- Sigstore integration time:
-
Permalink:
KovantAI/rstest@7dd28ae511ef3d0cfd706214423e3d4c25d55cfc -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/KovantAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7dd28ae511ef3d0cfd706214423e3d4c25d55cfc -
Trigger Event:
push
-
Statement type:
File details
Details for the file rstest-0.5.0-py3-none-win_amd64.whl.
File metadata
- Download URL: rstest-0.5.0-py3-none-win_amd64.whl
- Upload date:
- Size: 2.7 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e58cedbbea8953f2c4d27796b1dd239376baf3b0b79287c0827ae50942046229
|
|
| MD5 |
1e4453880fe8d8a5a46e46d90f2961b4
|
|
| BLAKE2b-256 |
62194aa10f6dae0b97f8c62e21addcc12ad5e6efc351f76b9300097a75b486c7
|
Provenance
The following attestation bundles were made for rstest-0.5.0-py3-none-win_amd64.whl:
Publisher:
release.yml on KovantAI/rstest
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rstest-0.5.0-py3-none-win_amd64.whl -
Subject digest:
e58cedbbea8953f2c4d27796b1dd239376baf3b0b79287c0827ae50942046229 - Sigstore transparency entry: 2742395178
- Sigstore integration time:
-
Permalink:
KovantAI/rstest@7dd28ae511ef3d0cfd706214423e3d4c25d55cfc -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/KovantAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7dd28ae511ef3d0cfd706214423e3d4c25d55cfc -
Trigger Event:
push
-
Statement type:
File details
Details for the file rstest-0.5.0-py3-none-musllinux_1_2_x86_64.whl.
File metadata
- Download URL: rstest-0.5.0-py3-none-musllinux_1_2_x86_64.whl
- Upload date:
- Size: 3.2 MB
- Tags: Python 3, musllinux: musl 1.2+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
80ff154398483999fc7bae419756f467c8ac9c3ceb0eba4de98a989b5f003eb1
|
|
| MD5 |
d2cb8e4e2e8a28906b0781413bd5bc33
|
|
| BLAKE2b-256 |
415d4b021a954acec0e5c1151163d6bf38bad91c9d70c29782f44d5f16d770b0
|
Provenance
The following attestation bundles were made for rstest-0.5.0-py3-none-musllinux_1_2_x86_64.whl:
Publisher:
release.yml on KovantAI/rstest
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rstest-0.5.0-py3-none-musllinux_1_2_x86_64.whl -
Subject digest:
80ff154398483999fc7bae419756f467c8ac9c3ceb0eba4de98a989b5f003eb1 - Sigstore transparency entry: 2742395228
- Sigstore integration time:
-
Permalink:
KovantAI/rstest@7dd28ae511ef3d0cfd706214423e3d4c25d55cfc -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/KovantAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7dd28ae511ef3d0cfd706214423e3d4c25d55cfc -
Trigger Event:
push
-
Statement type:
File details
Details for the file rstest-0.5.0-py3-none-musllinux_1_2_aarch64.whl.
File metadata
- Download URL: rstest-0.5.0-py3-none-musllinux_1_2_aarch64.whl
- Upload date:
- Size: 3.0 MB
- Tags: Python 3, musllinux: musl 1.2+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
10775bc981d7ee0829266051bd52794c9b0d4261f2d41674464707b40dee2ace
|
|
| MD5 |
ba6af33c12223453be2b8fe0eadc57e6
|
|
| BLAKE2b-256 |
6b478d99cb78b830b853085be2762aeedfc4ad9e5995bffb48da097d12c0071c
|
Provenance
The following attestation bundles were made for rstest-0.5.0-py3-none-musllinux_1_2_aarch64.whl:
Publisher:
release.yml on KovantAI/rstest
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rstest-0.5.0-py3-none-musllinux_1_2_aarch64.whl -
Subject digest:
10775bc981d7ee0829266051bd52794c9b0d4261f2d41674464707b40dee2ace - Sigstore transparency entry: 2742395145
- Sigstore integration time:
-
Permalink:
KovantAI/rstest@7dd28ae511ef3d0cfd706214423e3d4c25d55cfc -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/KovantAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7dd28ae511ef3d0cfd706214423e3d4c25d55cfc -
Trigger Event:
push
-
Statement type:
File details
Details for the file rstest-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: rstest-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 3.1 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ddcfe49687cb0bbed385e2c135d03b1adf50cf65bab7a92d06e17c6939952aa9
|
|
| MD5 |
a6a6aa83b1b9485af46596ecbb91819e
|
|
| BLAKE2b-256 |
a5e77d1d10d9bae4bbe6317b894e7b4d1081ba8a05ba500637a51bee298422f7
|
Provenance
The following attestation bundles were made for rstest-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release.yml on KovantAI/rstest
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rstest-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
ddcfe49687cb0bbed385e2c135d03b1adf50cf65bab7a92d06e17c6939952aa9 - Sigstore transparency entry: 2742395217
- Sigstore integration time:
-
Permalink:
KovantAI/rstest@7dd28ae511ef3d0cfd706214423e3d4c25d55cfc -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/KovantAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7dd28ae511ef3d0cfd706214423e3d4c25d55cfc -
Trigger Event:
push
-
Statement type:
File details
Details for the file rstest-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: rstest-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 3.0 MB
- Tags: Python 3, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
760e3ca99b077e0a5f6c873a24832eb6e87622955ef3192674ba962868e53cc6
|
|
| MD5 |
9374df4e3c1a4d0b448e8e1590c26d42
|
|
| BLAKE2b-256 |
a95a0ee36821eb3b4f9868f88942cd490ee86f39de027bc12e0cc565070fdc3d
|
Provenance
The following attestation bundles were made for rstest-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
release.yml on KovantAI/rstest
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rstest-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
760e3ca99b077e0a5f6c873a24832eb6e87622955ef3192674ba962868e53cc6 - Sigstore transparency entry: 2742395165
- Sigstore integration time:
-
Permalink:
KovantAI/rstest@7dd28ae511ef3d0cfd706214423e3d4c25d55cfc -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/KovantAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7dd28ae511ef3d0cfd706214423e3d4c25d55cfc -
Trigger Event:
push
-
Statement type:
File details
Details for the file rstest-0.5.0-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: rstest-0.5.0-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 2.8 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
321622ba641e7d02a3fd764d65b991d3b5c829ca12be2efb8239ae98a52497ad
|
|
| MD5 |
c136783235526df1dd4b9470c0472121
|
|
| BLAKE2b-256 |
ada528fc5b892f8da01ef5fe492b969c3a24d4af4d6772cce92cd9d01803257f
|
Provenance
The following attestation bundles were made for rstest-0.5.0-py3-none-macosx_11_0_arm64.whl:
Publisher:
release.yml on KovantAI/rstest
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rstest-0.5.0-py3-none-macosx_11_0_arm64.whl -
Subject digest:
321622ba641e7d02a3fd764d65b991d3b5c829ca12be2efb8239ae98a52497ad - Sigstore transparency entry: 2742395204
- Sigstore integration time:
-
Permalink:
KovantAI/rstest@7dd28ae511ef3d0cfd706214423e3d4c25d55cfc -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/KovantAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7dd28ae511ef3d0cfd706214423e3d4c25d55cfc -
Trigger Event:
push
-
Statement type: