# Mutation testing

Mutation testing introduces small synthetic bugs (*mutants*) into the code and
runs the test suite against each mutant. A suite that detects most mutants is
effective; one that misses many is not.

Mutation testing is **not** a way to find bugs in the code under test. It is a
way to find weaknesses in the tests.

## What it catches

- **Tests that exercise code without asserting on outcomes.** Coverage looks
  good; the mutation score reveals the test never checks anything that matters.
- **Vacuous assertions.** `assert x == x`, assertions on side effects that always
  hold, fixture leakage masking real behavior.
- **Test redundancy.** When two tests kill the same mutants, one is redundant in
  a stronger sense than coverage overlap can show, because killing a mutant
  accounts for assertions and not just reach. That makes it a candidate for
  [minimization](https://quality.stereobooster.com/test-suite-minimization.md).

What it does **not** catch: anything about the code itself.

## Mutation score vs coverage

[Coverage](https://quality.stereobooster.com/coverage.md) measures **reachability**: did a test execute this line?
It is blind to whether anything was asserted. Mutation score measures
**reachability × oracle-strength**: to *kill* a mutant a test must both reach
the mutated code *and* carry an assertion that distinguishes the mutant's
behavior from the original's. A mutant in unreached code simply survives, so
mutation testing subsumes the coverage signal and adds the dimension coverage
structurally omits.

- It is a sharper proxy *for assertion strength*: coverage cannot tell whether a
  reached line was checked at all.
- It is **not** a replacement for coverage's reachability signal. Using mutants
  to find the code the suite never touches means waiting for every mutant in
  that code to survive, the same fact coverage reports directly and far faster.
- It is far more costly: a naive run executes the whole suite once per mutant.

## Tools

- **Python:** [mutmut](https://github.com/boxed/mutmut), Cosmic Ray, mutpy.
- **JavaScript / TypeScript:** [Stryker](https://stryker-mutator.io/).
- **Rust:** cargo-mutants.
- **Java:** [PITest](https://pitest.org/) (bytecode-level).
- **C#:** Stryker.NET.
- **Go:** go-mutesting, gremlins.
- **Ruby:** mutant.
- **PHP:** Infection.

## When to use, when not

**Use:**

- As a periodic test-quality audit on critical modules, quarterly or per major
  release.
- As a refactor signal: if the mutation score drops on a module between releases,
  the tests rotted.
- As a one-number proxy for *assertion strength* on a bounded, important slice
  of code.

**Don't:**

- As a continuous CI gate on the whole codebase. Too slow; restrict to changed
  code or critical modules.
- On tests that rely on time, randomness, or external state. Mutation testing
  assumes deterministic runs.
- As a goal in itself. A high mutation score on bad tests is still bad tests; the
  goal is *the code is what it claims*, and mutation score is only a proxy for one
  ingredient of that.

## Evidence

- **The optimistic result.** Across 357 real, developer-fixed faults in five
  open-source projects (~321 KLOC), mutant detection correlated significantly
  with real-fault detection, independently of code coverage. This result
  established mutation score as a defensible substitute for hand-curated
  real-fault benchmarks (Just, Jalali, Inozemtseva, et al. 2014)[^just2014].
- **The confound.** A later study on CoreBench and Defects4J (large C and Java
  programs with real faults) found that the correlation between mutation score and
  real-fault detection is *weak once test-suite size is controlled for*
  (Papadakis et al. 2018)[^papadakis2018], the same deflation (Inozemtseva and Holmes 2014)[^inozemtseva2014] applied to coverage.
  The reading is symmetric: both coverage and mutation score are
  size-confounded proxies. Mutation score keeps a real edge (suites that score
  high *by mutation* detect significantly more faults than random suites of equal
  size), but as a raw correlate of correctness it is weak.
- **The assumption underneath.** Mutation testing rests on the *coupling effect*:
  the conjecture that tests catching simple, single-token faults also catch the
  complex faults built from them (DeMillo et al. 1978)[^demillo1978]. It is a well-supported working
  assumption, not a proven law: mutants are a stand-in for real faults, and the
  stand-in is imperfect.
- **The cost barrier.** Surveys spanning the field name the *equivalent-mutant*
  problem (mutants semantically identical to the original, which can never be
  killed and must be excluded by hand) as the main obstacle to routine use
  (Jia and Harman 2011; Papadakis et al. 2019)[^jia2011] [^papadakis2019].

## Classification

- **Quality dimensions:** Maintainability — measured via the test suite's effectiveness, not the code's correctness.
- **Area:** Test-suite quality measurement; any code with an example-test suite on critical modules; release-gate on business logic.

## Referenced by

- [Quality dimensions](https://quality.stereobooster.com/quality-dimensions.md) · Quality dimensions
- [Automated test generation](https://quality.stereobooster.com/automated-test-generation.md) · Methods
- [Measuring test-suite effectiveness](https://quality.stereobooster.com/measuring-test-effectiveness.md) · Methods
- [Test suite minimization](https://quality.stereobooster.com/test-suite-minimization.md) · Methods
- [The test suite as an object](https://quality.stereobooster.com/test-suite.md) · Methods
- [How AI fits into software quality](https://quality.stereobooster.com/ai.md) · AI
- [Conventional terminology](https://quality.stereobooster.com/terminology.md) · Conventional
- [Testing folklore](https://quality.stereobooster.com/testing-folklore.md) · Conventional
- [Choosing methods](https://quality.stereobooster.com/choosing.md) · Overview

## References

[^just2014]: Just, René, Darioush Jalali, Laura Inozemtseva, Michael D. Ernst, Reid Holmes, and Gordon Fraser. 2014. "[Are Mutants a Valid Substitute for Real Faults in Software Testing?](https://homes.cs.washington.edu/~rjust/publ/mutants_real_faults_tr_2014.pdf)" *Proceedings of the 22nd ACM SIGSOFT International Symposium on Foundations of Software Engineering (FSE '14)*, 654–65. <https://doi.org/10.1145/2635868.2635929>.
[^papadakis2018]: Papadakis, Mike, Donghwan Shin, Shin Yoo, and Doo-Hwan Bae. 2018. "[Are Mutation Scores Correlated with Real Fault Detection? A Large Scale Empirical Study on the Relationship Between Mutants and Real Faults](https://core.ac.uk/download/154760839.pdf)." *Proceedings of the 40th International Conference on Software Engineering (ICSE '18)*, 537–48. <https://doi.org/10.1145/3180155.3180183>.
[^inozemtseva2014]: Inozemtseva, Laura, and Reid Holmes. 2014. "[Coverage Is Not Strongly Correlated with Test Suite Effectiveness](https://www.cs.ubc.ca/~rtholmes/papers/icse_2014_inozemtseva.pdf)." *Proceedings of the 36th International Conference on Software Engineering (ICSE '14)*, 435–45. <https://doi.org/10.1145/2568225.2568271>.
[^demillo1978]: DeMillo, Richard A., Richard J. Lipton, and Frederick G. Sayward. 1978. "[Hints on Test Data Selection: Help for the Practicing Programmer](https://gse.ufsc.br/bezerra/disciplinas/Confiabilidade/docs/demillo-mutants.pdf)." *Computer* 11 (4): 34–41. <https://doi.org/10.1109/C-M.1978.218136>.
[^jia2011]: Jia, Yue, and Mark Harman. 2011. "[An Analysis and Survey of the Development of Mutation Testing](https://mutationtesting.uni.lu/TR-09-06.pdf)." *IEEE Transactions on Software Engineering* 37 (5): 649–78. <https://doi.org/10.1109/TSE.2010.62>.
[^papadakis2019]: Papadakis, Mike, Marinos Kintis, Jie Zhang, Yue Jia, Yves Le Traon, and Mark Harman. 2019. "[Mutation Testing Advances: An Analysis and Survey](https://mpapad.github.io/publications/pdfs/MutationSurvey2019.pdf)." In *Advances in Computers*, vol. 112. <https://doi.org/10.1016/bs.adcom.2018.03.015>.

## Acronyms

- KLOC — thousand lines of code
