Overview

PIT currently provides some built-in mutators, of which most are activated by default. The default set can be overridden, and different operators selected, by passing the names of the required operators to the mutators parameter. To make configuration easier, some mutators are put together in groups. Passing the name of a group in the mutators parameter will activate all mutators of the group.

Mutations are performed on the byte code generated by the compiler rather than on the source files. This approach has the advantage of being generally much faster and easier to incorporate into a build, but it can sometimes be difficult to simply describe how the mutation operators map to equivalent changes to a Java source file.

The operators are largely designed to be stable (i.e not be too easy to detect) and minimise the number of equivalent mutations that they generate. Those operators that do not meet these requirements are not enabled by default.

Additional operators are available from arcmutate.

Available mutators and groups

The following table list available mutators and whether or not they are part of a group :

Mutators “OLD_DEFAULTS” group “DEFAULTS” group “STRONGER” group “ALL” group
Conditionals Boundary yes yes yes yes
Increments yes yes yes yes
Invert Negatives yes yes yes yes
Math yes yes yes yes
Negate Conditionals yes yes yes yes
Return Values yes     yes
Void Method Calls yes yes yes yes
Empty returns   yes yes yes
False Returns   yes yes yes
True returns   yes yes yes
Null returns   yes yes yes
Primitive returns   yes yes yes
Remove Conditionals     EQUAL_ELSE case yes
Experimental Switch     yes yes
Inline Constant       yes
Constructor Calls       yes
Non Void Method Calls       yes
Remove Increments       yes
Experimental Argument Propagation       yes
Experimental Big Integer       yes
Experimental Member Variable       yes
Experimental Naked Receiver       yes
Negation       yes
Arithmetic Operator Replacement       yes
Arithmetic Operator Deletion       yes
Constant Replacement       yes
Bitwise Operator       yes
Relational Operator Replacement       yes
Unary Operator Insertion       yes

See the current code for current list (latest development version).


Default Mutators

Conditionals Boundary Mutator (CONDITIONALS_BOUNDARY)

Active by default

The conditionals boundary mutator replaces the relational operators <, <=, >, >=

with their boundary counterpart as per the table below.

Original conditional Mutated conditional
< <=
<= <
> >=
>= >

For example

if (a < b) {
  // do something
}

will be mutated to

if (a <= b) {
  // do something
}

Increments Mutator (INCREMENTS)

Active by default

The increments mutator will mutate increments, decrements and assignment increments and decrements of local variables (stack variables). It will replace increments with decrements and vice versa.

For example

public int method(int i) {
  i++;
  return i;
}

will be mutated to

public int method(int i) {
  i--;
  return i;
}

Please note that the increments mutator will be applied to increments of local variables only. Increments and decrements of member variables will be covered by the Math Mutator.

Invert Negatives Mutator (INVERT_NEGS)

Active by default

The invert negatives mutator inverts negation of integer and floating point variables. For example

public float negate(final float i) {
  return -i;
}

will be mutated to

public float negate(final float i) {
  return i;
}

Please note, this mutator does not mutate negative constants, only variables.

Math Mutator (MATH)

Active by default

The math mutator replaces binary arithmetic operations for either integer or floating-point arithmetic with another operation. The replacements will be selected according to the table below.

Original conditional Mutated conditional
+ -
- +
* /
/ *
% *
& |
| &
^ &
<< >>
>> <<
>>> <<

For example

int a = b + c;

will be mutated to

int a = b - c;

Keep in mind that the + operator on Strings as in

String a = "foo" + "bar";

is not a mathematical operator but a string concatenation and will be replaced by the compiler with something like

String a = new StringBuilder("foo").append("bar").toString();

Please note that the compiler will also use binary arithmetic operations for increments, decrements and assignment increments and decrements of non-local variables (member variables) although a special iinc opcode for increments exists. This special opcode is restricted to local variables (also called stack variables) and cannot be used for member variables. That means the math mutator will also mutate

public class A {
  private int i;

  public void foo() {
    this.i++;
  }
}

to

public class A {
  private int i;

  public void foo() {
    this.i = this.i - 1;
  }
}

See the Increments Mutator for details.

Negate Conditionals Mutator (NEGATE_CONDITIONALS)

Active by default

The negate conditionals mutator will mutate all conditionals found according to the replacement table below.

Original conditional Mutated conditional
== !=
!= ==
<= >
>= <
< >=
> <=

For example

if (a == b) {
  // do something
}

will be mutated to

if (a != b) {
  // do something
}

This mutator overlaps to a degree with the conditionals boundary mutator, but is less stable i.e these mutations are generally easier for a test suite to detect.

Return Values Mutator (RETURN_VALS)

This mutator has been superseded by the new returns mutator set. See Empty returns, False returns, True returns, Null returns and Primitive returns.

The return values mutator mutates the return values of method calls. Depending on the return type of the method another mutation is used.4

Return Type Mutation
boolean replace the unmutated return value true with false and replace the unmutated return value false with true
int byte short if the unmutated return value is 0 return 1, otherwise mutate to return value 0
long replace the unmutated return value x with the result of x+1
float double replace the unmutated return value x with the result of -(x+1.0) if x is not NAN and replace NAN with 0
Object replace non-null return values with null and throw a java.lang.RuntimeException if the unmutated method would return null

For example

public Object foo() {
  return new Object();
}

will be mutated to

public Object foo() {
  new Object();
  return null;
}

Please note that constructor calls are not considered void method calls. See the Constructor Call Mutator for mutations of constructors or the Non Void Method Call Mutator for mutations of non void methods.

Void Method Call Mutator (VOID_METHOD_CALLS)

Active by default

The void method call mutator removes method calls to void methods. For example

public void someVoidMethod(int i) {
  // does something
}

public int foo() {
  int i = 5;
  someVoidMethod(i);
  return i;
}

will be mutated to

public void someVoidMethod(int i) {
  // does something
}

public int foo() {
  int i = 5;
  return i;
}

Empty returns Mutator (EMPTY_RETURNS)

Active by default

Replaces return values with an ‘empty’ value for that type as follows

  • java.lang.String -> “”
  • java.util.Optional -> Optional.empty()
  • java.util.List -> Collections.emptyList()
  • java.util.Collection -> Collections.emptyList()
  • java.util.Set -> Collections.emptySet()
  • java.lang.Integer -> 0
  • java.lang.Short -> 0
  • java.lang.Long -> 0
  • java.lang.Character -> 0
  • java.lang.Float -> 0
  • java.lang.Double -> 0

Pitest will filter out equivalent mutations to methods that are already hard coded to return the empty value.

False returns Mutator (FALSE_RETURNS)

Active by default

Replaces primitive and boxed boolean return values with false.

Pitest will filter out equivalent mutations to methods that are already hard coded to return false.

True returns Mutator (TRUE_RETURNS)

Active by default

Replaces primitive and boxed boolean return values with true.

Pitest will filter out equivalent mutations to methods that are already hard coded to return true.

Null returns Mutator (NULL_RETURNS)

Active by default

Replaces return values with null. Methods that can be mutated by the EMPTY_RETURNS mutator or that are directly annotated with NotNull will not be mutated.

Pitest will filter out equivalent mutations to methods that are already hard coded to return null.

Primitive returns Mutator (PRIMITIVE_RETURNS)

Active by default

Replaces int, short, long, char, float and double return values with 0.

Pitest will filter out equivalent mutations to methods that are already hard coded to return 0.

Optional Mutators

Constructor Call Mutator (CONSTRUCTOR_CALLS)

Optional mutator that replaces constructor calls with null values. For example

public Object foo() {
  Object o = new Object();
  return o;
}

will be mutated to

public Object foo() {
  Object o = null;
  return o;
}

Please note that this mutation is fairly unstable and likely to cause NullPointerExceptions even with weak test suites.

This mutator does not affect non constructor method calls. See Void Method Call Mutator for mutations of void methods and Non Void Method Call Mutator for mutations of non void methods.

Inline Constant Mutator (INLINE_CONSTS)

The inline constant mutator mutates inline constants. An inline constant is a literal value assigned to a non-final variable, for example

public void foo() {
  int i = 3;
  // do something with i
}

Depending on the type of the inline constant another mutation is used. The rules are a little complex due to the different ways that apparently similar Java statements are converted to byte code.

Constant Type Mutation
boolean replace the unmutated value true with false and replace the unmutated value false with true
integer byte short replace the unmutated value 1 with 0, -1 with 1, 5 with -1 or otherwise increment the unmutated value by one. 1
long replace the unmutated value 1 with 0, otherwise increment the unmutated value by one.
float replace the unmutated values 1.0 and 2.0 with 0.0 and replace any other value with 1.0 2
double replace the unmutated value 1.0 with 0.0 and replace any other value with 1.0 3

For example

public int foo() {
  int i = 42;
  return i;
}

will be mutated to

public int foo() {
  int i = 43;
  return i;
}

Please note that the compiler might optimize the use of final variables (regardless whether those are stack variables or member variables). For example the following code

public class A {
  private static final int VAR = 13;
  
  public String foo() {
    final int i = 42;
    return "" + VAR + ":" + i;
  }
}

will be changed/optimized by the compiler to

public class A {
  public String foo() {
    return "13:42";
  }
}

In such situations the mutation engine can not mutate any variable.

Non Void Method Call Mutator (NON_VOID_METHOD_CALLS)

The non void method call mutator removes method calls to non void methods. Their return value is replaced by the Java Default Value for that specific type. See the table below.

Table: Java Default Values for Primitives and Reference Types

Type Default value
boolean false
int byte short long 0
float double 0.0
char '\u0000'
Object null

For example

public int someNonVoidMethod() {
  return 5;
}

public void foo() {
  int i = someNonVoidMethod();
  // do more stuff with i
}

will be mutated to

public int someNonVoidMethod() {
  return 5;
}

public void foo() {
  int i = 0;
  // do more stuff with i
}

and for method calls returning an object type the call

public Object someNonVoidMethod() {
  return new Object();
}

public void foo() {
  Object o = someNonVoidMethod();
  // do more stuff with o
}

will be mutated to

public Object someNonVoidMethod() {
  return new Object();
}

public void foo() {
  Object o = null;
  // do more stuff with o
}

Please note that this mutation is fairly unstable for some types (especially Objects where NullPointerExceptions are likely) and may also create equivalent mutations if it replaces a method that already returns one of the default values without also having a side effect.

This mutator does not affect void methods or constructor calls. See Void Method Call Mutator for mutations of void methods and Constructor Call Mutator for mutations of constructors.

Remove Conditionals Mutator (REMOVE_CONDITIONALS)

The remove conditionals mutator will remove all conditionals statements such that the guarded statements always execute

For example

if (a == b) {
  // do something
}

will be mutated to

if (true) {
  // do something
}

Although not currently enabled by default it is highly recommended that you enable it if you wish to ensure your test suite has full coverage of conditional statements.

As shown above the basic remove conditionals mutator ensures that the statements following the conditional always execute. It will also only mutate only equality checks (e.g. ==, !=).

Additional specialised versions of the mutator exist that will ensure the block never executes so

if (a == b) {
  // do something
}

will be mutated to

if (false) {
  // do something
}

If an else block is present it will always execute

if (a == b) {
  // do something
} else {
  // do something else
}

will be mutated to

if (false) {
  // do something
} else {
  // do something else
}

Specialisations also exist that will mutate the bytecode instructions for order checks (e.g. <=, >).

The available specialisations are:

The names reflect which branch will be forced to execute (the “if” or the “else”) and the type of checks that will be mutated.

The reason these are not enabled by default is that there is a large degree of overlap in the tests required to kill these mutations and those required to kill mutations from other default operators such as the conditional boundaries mutator.

Remove Increments Mutator (REMOVE_INCREMENTS)

Optional mutator that removes local variable increments.

Experimental Mutators

Experimental Argument Propagation (EXPERIMENTAL_ARGUMENT_PROPAGATION)

Experimental mutator that replaces method call with one of its parameters of matching type.

Experimental Big Integer (EXPERIMENTAL_BIG_INTEGER)

Experimental mutator that swaps big integer methods.

Experimental Naked Receiver (EXPERIMENTAL_NAKED_RECEIVER)

Experimental mutator that replaces method call with a naked receiver.

Experimental Member Variable Mutator (EXPERIMENTAL_MEMBER_VARIABLE)

The experimental member variable mutator mutates classes by removing assignments to member variables. The mutator can even remove assignments to final members. The members will be initialized with their Java Default Value for the specific type. See the table below.

Table: Java Default Values for Primitives and Reference Types

Type Default value
boolean false
int byte short long 0
float double 0.0
char '\u0000'
Object null

For example

public class MutateMe {
    private final int x = 5;
    //...
}

will be mutated to

  public class MutateMe {
    private final int x = 0;
    ...
  }

Please Note: This mutator is likely to create equivalent mutations if a member variable is explicitly initialized with the Java default value for the specific type of the member variable as in

public class EquivalentMutant {
    private int x = 0;
}

Experimental Switch Mutator (EXPERIMENTAL_SWITCH)

The switch mutator finds the first label within a switch statement that differs from the default label. It mutates the switch statement by replacing the default label (wherever it is used) with this label. All the other labels are replaced by the default one.

Negation Mutator (ABS)

This mutator replace any use of a numeric variable (local variable, field, array cell) with its negation. For example:

public float get(final float i) {
  return i;
}

will be mutated to

public float get(final float i) {
  return -i;
}

Arithmetic Operator Replacement Mutator (AOR)

Like the math mutator, this mutator replaces binary arithmetic operations for either integer or floating-point arithmetic with another operation. The mutator is composed of 4 sub-mutators (AOR_1 to AOR_4) that mutate operators according to the table below.

Original operator AOR_1 AOR_2 AOR_3 AOR_4
+ - * / %
- + * / %
* / % + -
/ * % + -
% * / + -

Arithmetic Operator Deletion Mutator (AOD)

This mutator replaces an arithmetic operation with one of its members. The mutator is composed of 2 sub-mutators, AOD_1 and AOD_2, that mutate the operation to its first and second member respectively. For example

int a = b + c;

will be mutated to

int a = b;

and to

int a = c;

Constant Replacement Mutator (CRCR)

Like the inline constant mutator, this mutator mutates inline constant. The mutator is composed of 6 sub-mutators (CRCR1 to CRCR6) that mutate constants according to the table below.

Constant CRCR1 CRCR2 CRCR3 CRCR4 CRCR5 CRCR6
c 1 0 -1 -c c+1 c-1

Bitwise Operator Mutator (OBBN)

This mutator mutates bitwise and (&) and or (|). It is composed of three sub-mutators, OBBN1, OBBN2 and OBBN3 that respectively reverse the operators, replace a bitwise operation by its first member, and by its second member. For example

a & b;

will be mutated to

a | b;

by OBBN1, to

a;

by OBBN2 and to

b;

by OBBN3.

Relational Operator Replacement Mutator (ROR)

This mutator replaces a relational operator with another one. The mutator is composed of 5 sub-mutators (ROR1 to ROR5) that mutate the operators according to the table below.

Original operator ROR1 ROR2 ROR3 ROR4 ROR5
< <= > >= == !=
<= < > >= == !=
> < <= >= == !=
>= < <= > == !=
== < <= > >= !=
!= < <= > >= ==

Unary Operator Insertion (UOI)

This mutator inserts a unary operator (increment or decrement) to a variable call. It affects local variables, array variables, fields, and parameters. It is composed of 4 sub-mutators, UOI1 to UOI4 that insert operators according to the table below.

Variable UOI1 UOI2 UOI3 UOI4
a a++ a– ++a –a

Thanks to Stefan Penndorf who contributed this documentation.


  1. Integer numbers and booleans are actually represented in the same way be the JVM, it is therefore never safe if change a 0 to anything but a 1 or a 1 to anything but a 0.
  2. Floating point numbers are always changed to 1 rather than adding 1 to the original value as this would result in equivalent mutations. Adding 1 to a large floating point number doesn’t necessarily change its value due to the imprecise way in which floats are represented.
  3. See note above which applies to both floats and doubles.
  4. The strategy used by this mutator was translated from code in the Jumble project