Find the bugs your tests would miss.
Documentation · Getting started · Coding agents · CI/CD · Configuration
pytestmut makes small, deliberate mistakes in your code, one at a time, and runs your pytest suite against each of them. If a test fails, the mistake was caught. If every test still passes, you have found a place where a real bug could hide, and pytestmut tells you which test would catch it.
Line coverage tells you which code your tests run. Mutation testing tells you which code your tests check.
$ pytestmut run
pytestmut 0.1.0: 15 mutants in 2 file(s), 3 tests, 12 worker(s), 2.3s
killed 8
survived 5
no coverage 2
score 53.3% (8 detected of 15 scored: 5 survived, 2 without coverage)
Survivors (5)
src/shop/pricing.py:8 cmp.boundary [7bf19b971895] in discount
- if amount > 100 and loyal:
+ if amount >= 100 and loyal:
Changing `>` -> `>=` at src/shop/pricing.py:8 in `discount` went unnoticed. Tests
that ran this code and still passed: tests/test_pricing.py::test_discount. No test
sits on the boundary of `>`. Add a case where both sides are equal and assert the
result; ...
Highlights
| ✅ Correct results first | A mutant only counts as caught when a test that normally passes fails with it active. Crashes, flaky tests and slow machines never count. |
| 🛡️ Safe to run | Your files are never modified. Test processes are contained, cleaned up and held to a memory budget, and credentials are kept out of their reach. |
| ⚡ Fast enough for every day | Each mutant runs only the tests that reach it, in parallel. Verdicts are cached between runs. |
| 🔒 Strict where it matters | Give critical code its own thresholds, mutation types and rules for exceptions. |
| 🤖 Built for review | Every missed mutant says what changed, which tests ran it, and what test would catch it, for people and coding agents alike. |
| 🔌 Fits your workflow | Terminal, pre-commit, CI, pytest --mutate, and HTML, markdown or JSON reports. |
Quick start
uv add --dev pytestmut # or: pip install pytestmut
pytestmut init --write # add a starter [tool.pytestmut] to pyproject.toml
pytestmut run # mutate, test, report
pytestmut show 7bf19b97 # one mutant, and what test would catch it
# pyproject.toml
[tool.pytestmut]
paths = ["src/mypackage"]
[tool.pytestmut.thresholds]
min_score = 80.0
The getting started guide takes an example project from its first run to every mutant caught.
What you can do with it
| Goal | How | Guide |
|---|---|---|
| Find weak tests in a project | pytestmut run |
Getting started |
| Test only what you changed | pytestmut run --diff main or --staged |
Choosing what to test |
| Judge specific tests | pytestmut run --tests tests/test_billing.py |
Choosing what to test |
| Demand more from critical code | [[tool.pytestmut.policy]] with its own thresholds |
Strict rules for critical code |
| Block weak commits | a pre-commit hook running pytestmut run --hook |
Pre-commit |
| Fail a pipeline below a threshold | pytestmut run in CI; the exit code is the gate |
CI/CD |
| Let a coding agent check its own tests | --tests, actionable guidance and JSON output |
Coding agents |
| Publish the report with your docs | the HTML report as a docs page | Reports in your docs site |
| Skip noise | built-in rules, # pytestmut: skip -- why, config |
Leaving code alone |
| Keep runs fast and light | test selection, cache, workers, memory budget | Speed and memory |
Reference: command line · configuration · mutation operators · how it works · troubleshooting
Why the numbers can be trusted
- Every kill is real. Verdicts come from a per-test record written inside pytest, not from exit codes. A must-die mutant proves the pipeline before any real one runs.
- Mutations never touch disk. They are applied in memory at import time, inside subprocesses too, so a crash can't leave mutated code behind.
- Ambiguity is resolved, not guessed. Timeouts are confirmed alone and against an unmutated run. Kills by order-dependent tests are checked. Survivors are re-checked against the whole suite.
- pytestmut tests itself. Its own verdict and policy logic is held to 100% with zero survivors, and the report is published with the docs.
Status
Alpha. Requires Python 3.10+ and pytest 8+. CI runs the full test suite on Linux, macOS and Windows with Python 3.10 to 3.14.
Development
uv sync
uv run nox # format, imports, licenses, lint, types, tests
uv run nox -s "Mutation Tests" # pytestmut on its own core modules
uv run nox -s "Serve Docs" # the docs, with fresh mutation and license reports
Releasing: bump __version__ in pytestmut/_meta.py and merge to main.
Once CI passes, the release workflow builds and checks the package, publishes
it to TestPyPI, verifies what TestPyPI serves, then publishes it to PyPI and
tags a GitHub release. Pushes that keep the version publish nothing.
License
Metadata
Release files for pytestmut 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pytestmut-0.1.0.tar.gz | 322.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pytestmut-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 483.4 kB
Release files / pytestmut-0.1.0.tar.gz
| Download URL | pytestmut-0.1.0.tar.gz |
|---|---|
| Size | 322.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cc28942d27af694f7ede7215c5d8613dffcdd8b6b8cd76d5d4e08e643ddb019a
|
|
BLAKE2b-256 checksum How to use checksums |
d84930010a0324874247240a3ecca4344aeeadb9b2c9daff8296d21cd6cd3b8f
|
| 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 Oct 14, 2026.
Transparency logRelease files / pytestmut-0.1.0-py3-none-any.whl
| Download URL | pytestmut-0.1.0-py3-none-any.whl |
|---|---|
| Size | 160.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7fcd7d6e1bcbd249fb48341a1b8db1689b59a66b2892a1e7e2e4c7ad21651f3c
|
|
BLAKE2b-256 checksum How to use checksums |
4499427c467e1956e5fadfee937067a1f804932e221e06d8a2b82efee51709ac
|
| 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 Oct 14, 2026.
Transparency log