Custom MethodMutatorFactory (external jar) is listed as available but generates 0 mutations — PIT 1.18.2

15 views
Skip to first unread message

Rahbar Zahid

unread,
Aug 27, 2026, 10:10:33 AM (11 days ago) Aug 27
to PIT Users

I've implemented a custom mutation operator as a standalone jar and registered it via META-INF/services. PIT lists it under "Available mutators" and accepts it by name in <mutators>, but it generates zero mutations: Created 0 mutation test units in pre scan, and with +export nothing is written. My operator's create() and the visitor's visitMethodInsn never appear to be invoked.

Environment: PIT 1.18.2, pitest-junit5-plugin 1.2.2, JDK 21, Maven, target project = Spring Petclinic.

What works:

  • PIT's built-in DEFAULTS mutators run fine on the same classes (157 mutations), so scanning/coverage is fine.
  • No "unknown mutator" error and no RUN_ERROR (after adding the operator jar to both the pitest plugin <dependencies> and the project's test-scope <dependencies>).
  • The operator's bytecode logic works correctly in isolation — a standalone ASM program using the same match + POP/POP/ACONST_NULL replacement successfully mutates the target class.

The operator targets repository.save(...) calls (owner ends with "Repository", name equals "save"):

```
public enum RemoveSaveCallMutator implements MethodMutatorFactory {
    REMOVE_SAVE_CALL_MUTATOR;
    public MethodVisitor create(MutationContext context, MethodInfo methodInfo, MethodVisitor mv) {
        return new RemoveSaveCallVisitor(this, context, mv);
    }
    public String getGloballyUniqueId() { return getClass().getName(); }
    public String getName() { return name(); }
}

```
----------------------------------------------------------------------------------------------------------------------------------------------
```
public class RemoveSaveCallVisitor extends MethodVisitor {
    private final MethodMutatorFactory factory;
    private final MutationContext context;
    public RemoveSaveCallVisitor(MethodMutatorFactory factory, MutationContext context, MethodVisitor mv) {
        super(Opcodes.ASM9, mv);
        this.factory = factory; this.context = context;
    }
    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
        if (owner.endsWith("Repository") && name.equals("save")) {
            MutationIdentifier id = context.registerMutation(factory, "Removed ORM persistence call: " + owner + "." + name);
            if (context.shouldMutate(id)) {
                super.visitInsn(Opcodes.POP);
                super.visitInsn(Opcodes.POP);
                super.visitInsn(Opcodes.ACONST_NULL);
                return;
            }
        }
        super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
    }
}

```
----------------------------------------------------------------------------------------------------------------------------------------------

Registered in META-INF/services/org.pitest.mutationtest.engine.gregor.MethodMutatorFactory containing org.example.RemoveSaveCallMutator (confirmed present in the built jar).

Question: Is there an additional step required in 1.18.2 for an externally-jarred custom operator to actually be invoked (e.g. a MutatorGroup, or registration in Mutator.java)? PIT clearly discovers the operator (it's listed and activatable), but never calls its visitor. What am I missing?

Reply all
Reply to author
Forward
0 new messages