Matching parameters base on generic types with Mockito

Viewed 368

I'm looking for a way to make a mock function return different values based on the generic type of its parameter. E.g, if the parameter is any instance of Generic<Foo> do something, and if the parameter is any instance of Generic<Bar> do something else.

I thought I'll be able to acomplish that using the following code:

@Mock
private Getter getter;

@Rule
public final Mocks mocks = new Mocks(this);

@Test
public void testSearch(){
    doReturn("Foo<Boolean>").when(getter).getValue(Matchers.<Foo<Boolean>>any());
    doReturn("Foo<String>").when(getter).getValue(Matchers.<Foo<String>>any());
    System.out.println(getter.getValue(new Foo<Boolean>()));
    System.out.println(getter.getValue(new Foo<String>()));
}

public static class Foo<T> {
}

public class Getter {
    <T> String getValue(BumapinRefOdsHandlerTest.Foo<T> foo) {
        return "not-used";
    }
}

However the code prints

Foo<String>
Foo<String>

What is the correct way to match parameters base on generic type?

2 Answers

You cant do this. During runtime, the type Foo<Boolean> and Foo<String> will be reduced to the same type Foo due to erasure. Eg the following assertion will hold

assert new Foo<Boolean>().getClass().equals(new Foo<String>().getClass())

There is no way to test a basic generic class for the type parameter used during compile, unless the class is a subclass and it can be inferred from the class definition, or it is fully specified in the method signature. eg

// Using reflection can recover that FooBoolean used type parameter Boolean
class FooBoolean extends Foo<Boolean> { ... }
// Using reflection on the Method can recover the type parameter of foo
void something(Foo<Boolean> foo) { ... }

Generics are not directly available at runtime. And in your case

doReturn("Foo<Boolean>").when(getter).getValue(Matchers.<Foo<Boolean>>any());
doReturn("Foo<String>").when(getter).getValue(Matchers.<Foo<String>>any());

will end up stubbing the method call with any parameter of type Foo. So the last stub wins, in your case doReturn("Foo<String>").

If you reverse the order to

doReturn("Foo<String>").when(getter).getValue(Matchers.<Foo<String>>any());
doReturn("Foo<Boolean>").when(getter).getValue(Matchers.<Foo<Boolean>>any());

the output will be

Foo<Boolean>
Foo<Boolean>
Related