How to use generic class with Mockito.when

I am trying to use a parameterized test and I want to use Mockito.when() the following scenario. I am not in a position where I can modify any code except for the test. I have put a comment on the line that is causing a compile error at the moment. I am having a hard time putting it into words, but basically I want to be able to mock the method without having access to the exact type during compile time.

Test class:

import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Map;

import static com.example.demo.Type.A;
import static com.example.demo.Type.B;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
public class MyTest {

    private static final Map<Type, Class<? extends MyStuff>> classByType = Map.of(A, StuffA.class, B, StuffB.class);

    @InjectMocks
    private Handler handler;

    @Mock
    private Converter converter;

    @Mock
    private Sender sender;

    @Captor
    private ArgumentCaptor<Thing> thingArgumentCaptor;

    @ParameterizedTest
    @EnumSource(value = Type.class)
    void testHandle(Type type) {
        MyStuff myStuff = mock(classByType.get(type));
        Thing thing = mock(Thing.class);
        when(myStuff.getType()).thenReturn(type);
        when(converter.convert(classByType.get(type).cast(type))).thenReturn(thing); // This line is causing the compile error

        handler.handle(myStuff);

        verify(sender).send(thingArgumentCaptor.capture());
        assertThat(thingArgumentCaptor.getValue()).isEqualTo(thing);
    }


}

Class under test:

public class Handler {

    private final Converter converter;
    private final Sender sender;

    public Handler(Converter converter, Sender sender) {
        this.converter = converter;
        this.sender = sender;
    }

    public void handle(MyStuff myStuff) {
        Thing thing;
        switch (myStuff.getType()) {
            case A:
                thing = converter.convert((StuffA) myStuff);
                break;
            case B:
                thing = converter.convert((StuffB) myStuff);
                break;
            default:
                throw new RuntimeException();
        }
        sender.send(thing);

    }
}

Domain object:

public abstract class MyStuff {
    public abstract Type getType();
}

Converter:

public interface Converter {
    Thing convert(StuffA myType);

    Thing convert(StuffB myType);
}

Sender:

public interface Sender {
    void send(Thing thing);
}

Type:

public enum Type {
    A, B
}

Is there any way to solve this without writing two separate test methods?

Answer

You ​can use reflection to call the appropriate method. Try replacing the line with the compilation error with the following two lines:

       ​Method method = Converter.class.getMethod("convert", classByType.get(type));
       ​when(method.invoke(converter, eq(myStuff))).thenReturn(thing);

If the type of the method parameter isn’t known at compile time, then reflection is the only way to go.