mutmut
mutmut is a Python mutation testing tool with an incremental workflow: mutate code, run relevant tests, inspect survivors, improve tests, and rerun.
Documentation URLs used to create this skill
- https://mutmut.readthedocs.io/en/latest/
- https://mutmut.readthedocs.io/en/latest/#mutmut---python-mutation-tester
- https://raw.githubusercontent.com/boxed/mutmut/master/README.rst
What mutmut is best for
- Finding weak assertions that code coverage misses.
- Improving unit-test precision by targeting survivors.
- Incremental mutation testing during normal development.
Requirements and platform caveats
mutmutrequires OS-levelforksupport.- On Windows, run via WSL.
- On some architectures (documented for
x86_64-darwin),libcstmay require Rust tooling (rustc,cargo) during install.
Install and first run
pip install mutmut
mutmut run
By default mutmut uses pytest and auto-discovers typical test/code locations.
Core workflow
- Run mutations:
mutmut run - Inspect/retest in TUI:
mutmut browse - Improve tests for surviving mutants.
- Retest from TUI (
r,f,m) or rerun from CLI. - Repeat until survivors are intentional or eliminated.
TUI shortcuts (documented)
r: rerun selected mutantf: retest functionm: retest module
Incremental behavior and state
mutmut stores progress in mutants/ and resumes work between runs. If you need a full restart, remove mutants/.
rm -rf mutants/
mutmut run
Running only specific targets (wildcards)
mutmut run "my_module*"
mutmut run "my_module.my_function*"
Use this for fast, focused loops while fixing a specific surviving mutant cluster.
Configuration
Configure in either:
setup.cfgunder[mutmut]pyproject.tomlunder[tool.mutmut](list-style values for path-like options)
setup.cfg example
[mutmut]
paths_to_mutate=src/
pytest_add_cli_args_test_selection=tests/
do_not_mutate=
*__tests.py
max_stack_depth=8
mutate_only_covered_lines=true
debug=false
also_copy=
conftest.py
pyproject.toml example
[tool.mutmut]
paths_to_mutate = ["src/"]
pytest_add_cli_args_test_selection = ["tests/"]
pytest_add_cli_args = ["-x"]
max_stack_depth = 8
mutate_only_covered_lines = true
High-value tuning knobs
1) Speed and relevance: max_stack_depth
Lower values:
- speed up runs
- reduce incidental-test coupling
- may increase survivors that broader integration paths would kill
2) File-level exclusion: do_not_mutate
Exclude fixtures/generated files/tests you intentionally do not mutate.
3) Coverage-gated mutation: mutate_only_covered_lines=true
Uses coverage signal to mutate only executed lines for finer-grained filtering.
4) pytest argument control
pytest_add_cli_args_test_selection: selection/deselection flagspytest_add_cli_args: other pytest flags/config overrides
Type-checker filtering (advanced)
mutmut can filter invalid mutants using mypy or pyrefly JSON output:
# pyrefly
type_check_command = ['pyrefly', 'check', '--output-format=json']
# mypy
type_check_command = ['mypy', 'your_package', '--output', 'json']
Important caveats from docs:
- This can reduce noise/perf cost, but may hide relevant test weaknesses.
- Supported type checkers: mypy and pyrefly.
pyright/tyare not supported for this mapping due to class-wide type-break effects.
Suppressing known non-actionable mutations
Use inline pragma:
VERSION = "1.2.3" # pragma: no mutate
Use this sparingly for truly low-value lines (for example, version constants).
Applying mutants to disk
From TUI or CLI:
mutmut apply <mutant>
Always commit your working tree first. Applying mutants modifies source on disk.
pre-commit integration
No official mutmut pre-commit hook repository is documented in upstream docs.
If a team still wants mutation checks in pre-commit, use a local hook (usually scoped/manual because full mutation runs are expensive):
repos:
- repo: local
hooks:
- id: mutmut-targeted
name: mutmut targeted run
entry: mutmut run "my_module*"
language: system
pass_filenames: false
Usage examples (input -> expected output)
Example 1: Basic quality pass
- Input: "Check whether my tests really verify behavior in
src/." - Command:
mutmut run - Expected output:
- Mutants generated/tested.
- Survivors available for triage in
mutmut browse.
Example 2: Focused fix loop
- Input: "Only mutate my parser function while I improve tests."
- Command:
mutmut run "mypkg.parser.parse_*" - Expected output:
- Only matching target region is exercised, giving faster feedback.
Example 3: Use coverage + stack depth tuning
- Input: "Mutation runs are too noisy and slow in this monorepo."
- Config:
mutate_only_covered_lines=true max_stack_depth=8 - Expected output:
- Fewer incidental test executions and more actionable survivors.
Troubleshooting
- If installs fail with
libcst/Rust errors, install Rust toolchain and retry. - If mutation scope is wrong, explicitly set
paths_to_mutate. - If runs are too slow, narrow target with wildcard and tune
max_stack_depth. - If too many irrelevant survivors, evaluate
do_not_mutate, line coverage filtering, and test selection args.
Practical guidance for an AI agent
When using this skill for a user request:
- Start with
mutmut runon a narrow scope if repo is large. - Inspect survivors in
mutmut browse. - Propose targeted test improvements for each survivor class.
- Rerun only relevant module/function wildcards for fast iteration.
- Escalate to full run once focused areas are green.