Link Search Menu Expand Document

Extended Mutation Operators

Requires pitest 1.11.0 or above.

Provided by the arcmutate-base plugin.

Maven Central

(note, extended operators were previously provided by a separate plugin).


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


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

e.g. for maven



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


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.


Removes the last argument to a varargs call, so

  public List<String> someStrings() {
    return asList("a", "b", "c");


  public List<String> someStrings() {
    return asList("a", "b");

Empty varargs calls will not be mutated.


introduced in 0.1.4

Swaps method calls for overloads which take one less parameter.




To avoid creating low quality mutants, this mutator only targets the following methods:

  • List.of
  • Set.of
  • EnumSet.of
  • LocalDateTime.of
  • LocalTime.of
  • io.reactivex.rxjava3.core.Flowable.of
  • io.reactivex.rxjava3.core.Observable.of


Removes calls to distinct for

  • java.util.Stream and its primitive specialisations
  • reactor.core.publisher.Flux
  • io.reactivex.rxjava3.core.Observable
  • io.reactivex.rxjava3.core.Flowable


Removes calls to filter for

  • java.util.Stream and its primitive specialisations
  • reactor.core.publisher.Flux
  • reactor.core.publisher.Mono
  • io.reactivex.rxjava3.core.Observable
  • io.reactivex.rxjava3.core.Flowable
  • io.reactivex.rxjava3.core.Maybe

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.


Removes calls to Stream.limit.

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


Removes calls to skip for

  • java.util.Stream and its primitive specialisations
  • reactor.core.publisher.Flux
  • io.reactivex.rxjava3.core.Observable
  • io.reactivex.rxjava3.core.Flowable


Removes calls to sorted and sort for

  • java.util.Stream and its primitive specialisations
  • reactor.core.publisher.Flux
  • io.reactivex.rxjava3.core.Observable
  • io.reactivex.rxjava3.core.Flowable


Removes calls to negate on predicates.


Removes calls to and on predicates.


Removes calls to or on predicates.


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.


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.


Swaps calls to Stream.allMatch for Stream.anyMatch


Swaps calls to or for and on predicates.


Swaps calls to and for or on predicates.


Improves the stability of mutations to the return statement of methods returning Mono, Flux, Maybe, Flowable or Observable. Pitest will mutate these to null, this mutator subsumes these mutants with mutations that return the corresponding empty() method.


To you use the plugin you must first acquire a licence.

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


Or for gradle

dependencies {
  pitest 'com.groupcdg.arcmutate:base:1.0.2'

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