Mutation Operators
dart_mutant includes 40+ mutation operators organized by category. Each operator represents a small, targeted change that a real bug might introduce.
Arithmetic Operators
Mutations that change mathematical operations.
| Original | Mutated To |
|---|---|
a + b |
a - b, a * b, a / b |
a - b |
a + b, a * b, a / b |
a * b |
a / b, a + b, a - b |
a / b |
a * b, a + b, a - b |
a % b |
a * b |
a++ |
a-- |
a-- |
a++ |
++a |
--a |
--a |
++a |
Example:
// Original
int calculate(int x, int y) => x + y;
// Mutant
int calculate(int x, int y) => x - y; // + → -
Comparison Operators
Mutations that change relational comparisons.
| Original | Mutated To |
|---|---|
a < b |
a <= b, a >= b, a > b |
a > b |
a >= b, a <= b, a < b |
a <= b |
a < b, a >= b, a > b |
a >= b |
a > b, a <= b, a < b |
a == b |
a != b |
a != b |
a == b |
Example:
// Original
bool isAdult(int age) => age >= 18;
// Mutant
bool isAdult(int age) => age > 18; // >= → >
Logical Operators
Mutations that change boolean logic.
| Original | Mutated To |
|---|---|
a && b |
a || b |
a || b |
a && b |
!a |
a (negation removed) |
Example:
// Original
bool canAccess(bool isAdmin, bool isOwner) => isAdmin && isOwner;
// Mutant
bool canAccess(bool isAdmin, bool isOwner) => isAdmin || isOwner; // && → ||
Boolean Literals
Direct true/false swaps.
| Original | Mutated To |
|---|---|
true |
false |
false |
true |
Example:
// Original
bool isEnabled = true;
// Mutant
bool isEnabled = false; // true → false
Null Safety Operators
Dart-specific null-aware mutations.
| Original | Mutated To |
|---|---|
a ?? b |
a (null coalescing removed) |
a?.b |
a.b (null-aware removed) |
a ??= b |
a = b (null-aware assignment) |
Example:
// Original
String getName(User? user) => user?.name ?? 'Anonymous';
// Mutant 1
String getName(User? user) => user.name ?? 'Anonymous'; // ?. → .
// Mutant 2
String getName(User? user) => user?.name; // ?? removed (returns null)
Control Flow
Mutations that affect branching and loops.
| Original | Mutated To |
|---|---|
if (condition) |
if (true) |
if (condition) |
if (false) |
while (condition) |
while (false) |
break |
(removed) |
continue |
(removed) |
Example:
// Original
void process(bool shouldRun) {
if (shouldRun) {
doWork();
}
}
// Mutant 1
void process(bool shouldRun) {
if (true) { // condition → true
doWork();
}
}
// Mutant 2
void process(bool shouldRun) {
if (false) { // condition → false
doWork();
}
}
String Literals
Mutations for string values.
| Original | Mutated To |
|---|---|
"any string" |
"" (empty string) |
"" |
"mutant" |
Note: String mutations are limited to avoid noise. Only strings in meaningful positions are mutated.
Assignment Operators
Compound assignment mutations.
| Original | Mutated To |
|---|---|
a += b |
a -= b |
a -= b |
a += b |
a *= b |
a /= b |
a /= b |
a *= b |
Return Values
Mutations that change what functions return.
| Original | Mutated To |
|---|---|
return value |
return null (for nullable types) |
return true |
return false |
return false |
return true |
Excluded from Mutation
dart_mutant automatically excludes:
- Generated code:
*.g.dart,*.freezed.dart,*.gr.dart - Comments: Both line and block comments
- Import/export statements: Package imports
- Annotations:
@override,@deprecated, etc. - Constant expressions:
constvalues that would break compilation
Next Steps
- Filtering - Control what gets mutated
- Interpreting Results - Understand surviving mutants