mutation-testing

Installation
SKILL.md

Mutation Testing

Concept of the skill

Mutation testing is the behavioral signal of test-suite quality. The tool automatically modifies the production code by small, syntactically-valid changes called mutants — replace < with <=, negate a condition, flip a Boolean, alter a constant from 42 to 43, delete a statement, swap a return value for null, no-op a void method call — and runs the test suite against each modified version. If the tests fail on a mutant, the mutant is killed — the tests caught the change. If the tests still pass, the mutant survived — the tests do not actually verify the behavior at that code location, even if coverage said they reached it. The mutation score is killed / (total − equivalent mutants), where equivalent mutants are syntactic changes that produce no observable behavior difference (5-15% noise typical).

Replaces coverage-as-quality-signal with a direct behavioral verification signal. Solves the problem that high code coverage is a weak indicator of test effectiveness — Inozemtseva & Holmes (2014) showed coverage is not strongly correlated with test-suite effectiveness, which is why teams routinely ship coverage-90% codebases with bugs slipping through. Coverage asks "did the tests reach this line?"; mutation asks "would the tests catch a defect here?" — the second question is closer to what we care about, and the answer is more specific: each survived mutant is a directly addressable test-suite gap with a known location and a known kind of defect (off-by-one, condition negation, sign error, missing return assertion). Just et al. (2014) validated that mutation score correlates with real fault-detection rate, making it a meaningful proxy where coverage falls short.

Distinct from test-coverage-strategy, which owns the structural signal of which code the test suite reaches — coverage is a necessary precondition for mutation testing to apply (an uncovered mutant trivially survives because no test runs on it); coverage is the floor, mutation is the next layer. The two compose into a mature test-quality strategy: coverage as floor, mutation as verification. Distinct from test-doubles-design, which owns the construction of mocks/stubs/fakes/spies — this skill measures whether tests built with them actually verify behavior. Distinct from testing-strategy, which owns level choices (unit/integration/e2e) — mutation is a measurement applied at any level. Distinct from chaos-engineering, which is runtime fault injection into a deployed system — mutation is build-time source-code mutation. Distinct from fuzz-testing, which varies inputs to find crashes — mutation varies the program to find untested behaviors. Distinct from test-driven-development, which produces tests with high behavioral specificity as a side effect — mutation testing is one way to measure whether that specificity is actually present. Mutation testing is to a test suite what a fire drill is to a building's evacuation plan — you do not measure preparedness by counting how many exits exist (coverage), you measure it by deliberately staging a fire and watching whether anyone notices in time (mutation kill rate). An exit that nobody walks through during the drill is not really an exit, regardless of how prominently it is signposted. The wrong mental model is that the mutation score is a target to engineer toward — a number to push up the way teams push up coverage, with hard merge-gates on it. It is not. The discipline is not in maximizing the score; it is in reading the survived-mutant list. Each survivor is one of four things: (1) a real test gap — the mutant alters observable behavior and no test catches it; action: write the missing test; (2) an equivalent mutant — the syntactic change has no observable effect; action: mark as equivalent and exclude; (3) an intentional non-test — the code is intentionally unverified (defensive check, log message, debug path); action: annotate, consider whether the policy should change; (4) off-scope code — generated, vendor, scaffolding; action: exclude from analysis. Treating the score as a hard merge-gate without classifying survivors produces Goodharted tests engineered to satisfy the metric without verifying meaningful behavior, plus team frustration from equivalent-mutant noise (5-15%) being misread as real defect signal. The survival's kind (which operator caused which survivor) is part of the diagnostic, not just the count.

Coverage

The behavioral test-suite quality measurement that introduces small syntactically-valid modifications (mutants) to production code and checks whether the test suite distinguishes the mutant from the original. Covers the mutant-operator vocabulary (arithmetic, relational, conditional, logical, constant, statement deletion, return value, method call removal), the kill-or-survive primitive, the mutation score metric, the equivalent-mutant problem and detection heuristics, selective mutation (Offutt et al.'s subset), execution strategies (full / incremental / bytecode / distributed), the modern tooling ecosystem (PIT, Stryker, mutmut, etc.), and the strategic distinction between mutation score as a target (anti-pattern) and the survived-mutant list as a to-do (correct use).

Philosophy of the skill

Mutation testing inverts the coverage question. Coverage asks: did the test reach this line? Mutation asks: would the test catch a defect at this line? The second question is closer to what we actually care about, and the answer is more specific: each survived mutant is a directly addressable test-suite gap with a known location and a known kind of defect.

The discipline is not in maximizing the score; it is in reading the survived-mutant list. Each survivor is either a real gap (the test suite does not verify this behavior — write the test), an equivalent mutant (the syntactic change has no observable effect — exclude it), or an intentional non-test (defensive check, log string, debug path — note it as intentional). Working through the list with this classification produces a stronger test suite without engineering tests to satisfy the metric.

Mutation testing's modern feasibility is what makes it strategic. A decade ago the technique was largely impractical for large codebases. Today's tools — PIT's bytecode mutation, Stryker's incremental analysis, distributed execution, test prioritization — run mutation testing in CI in minutes for codebases of hundreds of thousands of lines. The cost barrier that historically pushed teams to coverage as a substitute is largely gone.

Installs
3
First Seen
May 18, 2026
mutation-testing — jacob-balslev/skills