Skip to main content
pytestmut

Find the bugs your tests would miss.

CI Docs Python pytest Self-test mutation score Types License: MIT Code style

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

MIT

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)

Source distribution for pytestmut 0.1.0
File Size Uploaded
pytestmut-0.1.0.tar.gz 322.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pytestmut 0.1.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 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