Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

FlagPurposeExample
(none)Run mutation testing and print resultspretender mutation src/
--dry-runList mutation sites without running testspretender mutation src/ --dry-run
--score-min NExit non-zero if score < N%pretender mutation src/ --score-min 80
--format jsonEmit structured JSON reportpretender mutation src/ --format json

Tool selection

pretender detects the dominant language from the target paths and selects the appropriate tool automatically:

LanguageToolInstall
Pythonmutmutpip install mutmut
Rustcargo-mutantscargo install cargo-mutants
JavaScript / TypeScriptStrykernpm 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-run queries cargo mutants --list and 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

FieldTypeDescription
toolstringName of the mutation tool used (e.g. "cargo-mutants")
totalintegerTotal number of mutants generated
killedintegerMutants caught by the test suite
survivedintegerMutants not caught (test gaps)
score_pctfloatMutation score: (killed / total) * 100. Returns 100.0 when no mutants are generated
survivorsarrayList of surviving mutant descriptors (see below)

Surviving mutant fields

FieldTypeDescription
filestringSource file containing the mutant
lineintegerLine number of the mutation
descriptionstringHuman-readable description of the applied mutation
missed_testsarray of stringsTest 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.