Mutations
A mutation is a tiny change in your code. Gooze applies one mutation and runs tests to see if tests notice.
Statuses
Each mutation ends in a status. Simple meaning:
- Killed: tests failed for the mutation (usually good)
- Survived: tests still passed (usually means weak/missing assertion)
- Skipped: Gooze did not run it (example: no matching test)
- Not covered: the mutated line is not covered by the coverage profile passed with
--coverage-profile, so Gooze skipped its tests. It counts as a survivor (it lowers the score) but is tallied separately. - Error: Gooze had an unexpected error
About timeouts: different Gooze versions can show timeouts differently. If you see many suspicious kills, check logs and try --verbose.
Mutation types
These six types run by default. Each one makes a single, mechanical change to one spot in your code, then your tests run against it. If the tests still pass, the mutation survived — a gap in coverage. Each example below is shown as a diff: the - line is your code, the + line is what Gooze runs instead.
Quick reference:
arithmetic + - * / % each -> every other operator
comparison < > <= >= == != each -> every other operator
logical && || && <-> ||
boolean true false true <-> false
numbers 5, 2.5, ... literal -> 0 and 1
unary - + ! ^ - <-> + ; ! and ^ removedarithmetic — + - * / %
Swaps a binary math operator. Catches off-by-operator bugs and results that are computed but never asserted.
Rule: each operator becomes every other arithmetic operator (- shown; also *, /, %)
- sum := a + b
+ sum := a - bIf it survives: no test checks the numeric result of this expression.
comparison — < > <= >= == !=
Swaps a relational operator. Great at exposing untested boundaries (off-by-one) and branches that are never taken in tests.
Rule: each operator becomes every other comparison operator (>= shown; also >, <=, ==, !=)
- if a < b {
+ if a >= b {If it survives: a boundary or the two branches of this condition aren't both exercised.
logical — && ||
Flips AND/OR. Reveals conditions where one operand never actually decides the outcome in your tests.
Rule: && becomes ||, and || becomes &&
- if a && b {
+ if a || b {If it survives: one side of the condition is never the deciding factor in any test.
boolean — true false
Inverts a boolean literal. Surfaces feature flags and defaults whose effect is never asserted.
Rule: true becomes false, and false becomes true
- enabled := true
+ enabled := falseIf it survives: the value of this flag doesn't change any tested behavior.
numbers — int & float literals
Replaces a numeric constant with 0 and 1. Finds magic numbers (limits, sizes, rates) that no test pins down.
Rule: a numeric literal becomes 0 and 1 (0 shown)
- limit := 5
+ limit := 0If it survives: the exact value of this constant doesn't matter to any test.
unary — - + ! ^
Flips a sign or drops a negation. Catches missing checks on negation, sign, and bitwise complement.
Rule: -x becomes +x (and vice versa); ! and ^ are removed
- ok := !flag
+ ok := flagIf it survives: the negation/sign here isn't checked by a test.
The engine also contains branch (replace a condition with true/false), statement (delete a statement), and loop (loop boundaries and body removal) mutators; these are not part of the default set.
Skip a line or block with the bypass annotation.
What to do when you see a survivor
A survivor is a hint. It usually means: your tests do not check this behavior.
- Open the diff in
gooze report view. - Ask: what behavior changed?
- Add a test that would fail if this behavior changes.
- Run Gooze again and see if it is killed now.
gooze report view