Mutalisk mutates your source, then runs your tests against each mutated version. A mutant that your tests catch is killed; one they don't is survived — a gap in your test suite.

Mutators are split into two tiers. Default-on mutators run with a bare MIX_ENV=test mix mut. Opt-in mutators run only when you ask for them; they are either noisier (more equivalent/false-positive mutants) or niche.

Targets and mutator names are independent gates. --mutators picks which mutators to consider; --enable replaces the enabled target families. If a mutator's target is not enabled by default, pass both, for example --enable module_attribute --mutators attribute_literal. If the target is already enabled by default (dispatch, guard, env_walker, pattern_shape), naming the mutator is enough. Enabling a target without naming a mutator runs the default selectable mutator set unlocked by that target; a few experimental mutators still require --mutators explicitly.

Default-on

These focus on common faults while limiting equivalent or invalid mutants.

MutatorWhat it does
Arithmeticswaps + - * / (e.g. a + b → a - b)
ComparisonBoundaryoff-by-one on comparisons (< → <=, > → >=, …)
ComparisonNegationnegates comparisons (== → !=, < → >=, …)
Booleanswaps boolean operators (and ↔ or, && ↔ ||)
UnaryNotdrops ! / not on boolean expressions
GuardComparisonBoundarycomparison-boundary, inside when guards
GuardComparisonNegationcomparison-negation, inside when guards
GuardTypeTestswaps guard type tests (is_integer → is_float, …)
AtomLiteralrotates known atoms (:ok ↔ :error, :lt/:gt/:eq)
IntegerLiteralinteger-literal change in pattern position (1 → 0)
ConcatOperator++ → -- on list concatenation
Pinunpins a pin in a pattern (^x → x)
FunctionReplaceclosed-allowlist stdlib swaps (Enum.min↔max, filter↔reject, List.first↔last, String.starts_with?↔ends_with?, …)

Opt-in

Reach for these when you want a deeper check and can tolerate more equivalent/noise mutants. Enable a whole non-default target with --enable <target>, or run just one mutator with --mutators <name> plus --enable <target> when that target is not enabled by default. Names are comma-separated and use the snake_case module name, e.g. negate_conditional.

MutatorTargetWhat it does
AttributeLiteralmodule_attributemutate module-attribute literal values
IntegerLiteral (body) / BooleanLiteralbody_literalinteger/boolean literals in function bodies
AtomLiteral / BooleanLiteral / StringLiteral / NilLiteral (pattern)pattern_literalopt-in pattern-position literal mutations; IntegerLiteral in patterns is already default-on
StringLiteralenv_walker"" / "x" string-literal swaps
FloatLiteralenv_walkerfloat-literal change
NilLiteralenv_walkernil-literal swaps
CollectionEmptyenv_walkerempty a list / 2-tuple / map / n-tuple (never a struct)
VariableReplacevariableswap a variable reference for another in scope
VariableToLiteralvariablereplace a typed variable reference with a boundary literal; requires explicit --mutators variable_to_literal
BitwiseOperatordispatchbitwise op swaps (band/bor/bsl/…)
Membershipdispatchin → not in
NegateConditionalconditionalif/unless condition: negate (!cond) / force true / force false
StatementDeletestatement_deletedelete a non-last statement from a function body
ClauseDeleteclause_deletedelete a clause from case / cond / with / receive / try
GuardBooleanguard_booleanand ↔ or, drop not, inside when guards
PipelineDropStagepipeline_dropdrop a middle stage from a |> chain
MapUpdateDropmap_update_drop%{m | k: v} → m (drop the update)
ReceiveTimeoutreceive_timeoutmutate receive … after t (t → 0 / :infinity / drop)

Examples:

# run the body-literal target only
MIX_ENV=test mix mut --enable body_literal

# run only NegateConditional + ClauseDelete
MIX_ENV=test mix mut --enable conditional,clause_delete \
  --mutators negate_conditional,clause_delete

Why some mutators are opt-in

Literal, deletion, and structural mutations can produce more equivalent mutants: changes that no test can distinguish because the program behaves the same. Enable them when their extra depth is useful, then assess survivors against the intended behaviour of your code.