Link Search Menu Expand Document

Extended Mutation Operators

Requires pitest 1.7.0 or above.

Maven Central

Background

Pitest ships with a set of carefully selected default operators. They give good confidence in a test suite, while avoiding creating large numbers of equivalent or low quality mutants. Although pitest provides a number of other operators, they are not enabled by default as they may provide a poorer experience.

The default pitest operators were originally designed for use with Java 5. They are still useful, but modern Java code looks very different from what we used to write, with fewer loops and conditionals. Often the default mutator set is not able to mutate it usefully.

The extended operators have been designed to mutate code written in a modern style using apis introduced in Java 8 and later. Like the default set, they are low noise and work hard to avoid creating obviously equivalent mutants.

In addition, the plugin adds subsumption analysis to pitest. This reduces the number of mutations produced without reducing the strength of the analysis. This is achieved by identifying mutants that would always be killed by the same tests as other mutants generated for a class.

Mutator Groups

EXTENDED

The recommended set of extended operators. These should be used in combination with the built in DEFAULT or STRONGER set.

e.g. for maven

<configuration>
  <mutators>
    <mutator>STRONGER</mutator>
    <mutator>EXTENDED</mutator>
  </mutators>
</configuration>

EXTENDED_ALL

The recommended extended operators, plus additional operators that we do not yet recommend enabling by default as they may not result in a smooth experience.

Mutators from this group may be moved to the EXTENDED group as we gather reports from more code bases and introduce additional static analysis to avoid issues.

The Operators

CHAINED_CALLS

Removes calls to methods with same return type as owner, as are commonly seen when using the Builder Pattern.

For example, the following code would be mutated at the indicated points, while standard pitest would only mutate the method return value to null.

  public Widget provides() {
    return Widget.named("foo") // change to return null (standard mutation)
      .withDescription("A widget") // call removed
      .withOnByDefault(true) // call removed
      .withChildren(asList("a", "b"); // call removed
  }

This mutator is similar to the experimental NAKED_RECEIVER operator in standard pitest, but is aware of Java generics. Analysis is also performed to remove low quality mutants from this operator.

REMOVE_DISTINCT

Removes calls to Stream.distinct.

REMOVE_FILTER

Removes calls to Stream.filter.

Logic implemented in stream filters is often already mutated by the standard returns mutator when the predicate passed to the filter is a lambda or method reference within the same class.

This operator fills the gap when the filter is passed a method reference to a different class. For example, standard pitest would not mutate the following code

Stream<File> fileLogic(Stream<File> files) {
   return files
     .filter(File::exists) // call removed by extended operator
     .filter(File::isDirectory); // call removed by extended operator
} 

When using the extended operators a subsumption analysis is run to remove redundant mutants. return true mutants in lambdas will be suppressed when a remove filter mutant subsumes it, and remove filter mutants will be suppressed when subsumed by a return true mutant in a referenced method in the same class.

REMOVE_LIMIT

Removes calls to Stream.limit.

To avoid creating timeouts, this mutant is suppressed when the stream it acts on is clearly infinite.

REMOVE_SKIP

Removes calls to Stream.skip.

REMOVE_SORTED

Removes calls to Stream.sorted.

REMOVE_PREDICATE_NEGATION

Removes calls to negate on predicates.

REMOVE_PREDICATE_AND

Removes calls to and on predicates.

REMOVE_PREDICATE_OR

Removes calls to or on predicates.

FIELD_WRITES

Removes writes to non static fields.

This operator is not included in the main EXTENDED group as it highlights issues on the setter methods of Java beans. Although these are valid and valuable mutants in most contexts, some teams choose to write them pre emptively, even though they are not used within the code. The mutator is therefore not enabled by default, but you may wish to enable it. If you have unused setter methods they can be excluded from the mutation analysis using pitest’s excludedMethods parameter.

SWAP_PARAMS

Swaps the last two parameters of a method call if they have the same type.

void foo(int enemy, int friend) {
   fireMissilesAvoidingAlly(enemy, friend); // mutates to fireMissilesAvoidingAlly(friend, enemy); 
}

Mutations are not created when the same parameter is passed at both positions, or where generic types are mismatched.

SWAP_ANY_MATCH

Swaps calls to Stream.anyMatch for Stream.allMatch

SWAP_PREDICATE_OR

Swaps calls to or for and on predicates.

SWAP_PREDICATE_AND

Swaps calls to and for or on predicates.

Installation

To you use the plugin you must first acquire a licence. If you are working on an open source project you can get a free licence here. For commercial projects we are currently accepting requests for our beta programme.

The licence file must be named cdg-pitest-licence.txt and placed at the root of the project.

The plugin itself should be placed on the classpath of the pitest build integration plugin.

E.g for maven

<plugin>
  <groupId>org.pitest</groupId>
  <artifactId>pitest-maven</artifactId>
  <version>1.7.3</version>
    <dependencies>
      <dependency>
        <groupId>com.groupcdg.pitest</groupId>
        <artifactId>extended-mutators</artifactId>
        <version>0.0.8</version>
      </dependency>
    </dependencies>
</plugin>

Or for gradle

dependencies {
  pitest 'com.groupcdg.pitest:pitest-extended-mutators:0.0.8 number'
}

Individual operators or groups should then be enabled using the standard pitest configuration options.