Mutation Testing
Mutation testing measures test-suite effectiveness by injecting small code changes (“mutants”) and checking whether the tests catch them. A high mutation score means your tests fail when the code is wrong — a low score means gaps exist even when line coverage looks healthy.
pretender mutation <paths...> wraps language-specific mutation testing tools
and reports results in a unified format. Each tool must be installed separately
— pretender invokes them as subprocesses.
Quick reference
| Flag | Purpose | Example |
|---|---|---|
| (none) | Run mutation testing and print results | pretender mutation src/ |
--dry-run | List mutation sites without running tests | pretender mutation src/ --dry-run |
--score-min N | Exit non-zero if score < N% | pretender mutation src/ --score-min 80 |
--format json | Emit structured JSON report | pretender mutation src/ --format json |
Tool selection
pretender detects the dominant language from the target paths and selects the appropriate tool automatically:
| Language | Tool | Install |
|---|---|---|
| Python | mutmut | pip install mutmut |
| Rust | cargo-mutants | cargo install cargo-mutants |
| JavaScript / TypeScript | Stryker | npm install --save-dev @stryker-mutator/core |
If a project contains multiple languages, pretender uses the language of the majority of files in the given paths.
Java (PIT) — not yet automated
pretender mutation does not yet invoke PIT directly. Run mutation coverage
manually with Maven or Gradle:
mvn org.pitest:pitest-maven:mutationCoverage
# or
gradle pitest
--dry-run still works for Java files — pretender uses tree-sitter to list
candidate mutation sites without invoking PIT.
Basic usage
# Run mutation testing against a package
pretender mutation src/
# Preview mutation sites without running tests
pretender mutation src/ --dry-run
# Fail if mutation score falls below a threshold
pretender mutation src/ --score-min 80
# Emit results as JSON
pretender mutation src/ --format json
--dry-run: preview mutation sites
Lists every planned mutation without executing the test suite. Useful for understanding coverage before committing to a full run.
pretender mutation src/ --dry-run
Example output:
src/engine.rs:42 replace operator `>`
src/engine.rs:67 replace return value
src/config.rs:15 replace operator `+`
Note: For Rust,
--dry-runqueriescargo mutants --listand reflects the real mutation plan. For all other languages (Python, JavaScript, TypeScript, Java), it uses a built-in tree-sitter analysis that enumerates operator and return-value sites — the count and descriptions are approximate and may differ from what the underlying tool would produce.
--score-min: CI gate
Exits non-zero if the achieved mutation score is below the given percentage. Use this in CI to enforce a minimum quality bar.
pretender mutation src/ --score-min 80
The exit code is 0 when the score meets or exceeds the threshold, 1
otherwise.
--format json: machine-readable output
Emits a MutationReport JSON object to stdout.
pretender mutation src/ --format json
MutationReport fields
| Field | Type | Description |
|---|---|---|
tool | string | Name of the mutation tool used (e.g. "cargo-mutants") |
total | integer | Total number of mutants generated |
killed | integer | Mutants caught by the test suite |
survived | integer | Mutants not caught (test gaps) |
score_pct | float | Mutation score: (killed / total) * 100. Returns 100.0 when no mutants are generated |
survivors | array | List of surviving mutant descriptors (see below) |
Surviving mutant fields
| Field | Type | Description |
|---|---|---|
file | string | Source file containing the mutant |
line | integer | Line number of the mutation |
description | string | Human-readable description of the applied mutation |
missed_tests | array of strings | Test names that should have caught this mutant but did not. Populated by mutmut only; always [] for cargo-mutants and Stryker, which do not report per-mutant test attribution |
Example (mutmut, which populates missed_tests):
{
"tool": "mutmut",
"total": 120,
"killed": 102,
"survived": 18,
"score_pct": 85.0,
"survivors": [
{
"file": "src/engine.py",
"line": 42,
"description": "replace `>` with `>=`",
"missed_tests": ["test_threshold_boundary"]
}
]
}
CI integration
The recommended way to run mutation testing in CI is through the workflow
generated by pretender ci generate github:
pretender ci generate github
This writes .github/workflows/pretender.yml. The generated file includes a
check step and a mutation gate step:
- name: pretender check
run: pretender check .
- name: Mutation score gate
run: pretender mutation . --score-min 60
The --score-min value defaults to 60 and can be set per invocation or via
pretender mutation . --score-min <N> in your CI workflow.