I have two generic objects of class Generic<T, R>
:
Generic<TypeA, TypeB> genericOne;
Generic<TypeB, TypeC> genericTwo;
I have to mock:
when(someObject.someMethod(Matchers.<Generic<TypeA, TypeB>>any()))
.thenReturn(responseOne());
when(someObject.someMethod(Matchers.<Generic<TypeB, TypeC>>any()))
.thenReturn(responseTwo());
The problem is that mockito will not the see difference between this two method calls because of type erasure - it's both recognized as Generic<T, R>
class.
Is there any way to to distinguish between this two method calls?
Mockito doesn't know which generics was specified in front of any()
invocation and anyway it doesn't matter.
The parameters expected in the recording a mock behavior has to rely on :
equals()
equals()
is not adequateany()
if the value of the parameter in the mock invocation doesn't matter or is not known in the test fixtureThese never have to rely on the generic specified on.
To have a good example, look at Mockito.anyCollectionOf(Class<T> clazz)
or Mockito.anyMapOf(Class<K> keyClazz, Class<V> valueClazz)
.
These methods know the class passed but anyway, their specifications state :
This method don't do any type checks, it is only there to avoid casting in your code. This might however change (type checks could be added) in a future major release.
Generally what you want to check is that the method to mock was invoked with the expected parameter and not any parameter : so any()
will never achieve it.
So trying to check only the generic type but accepting any values as parameters seems be an anti pattern mocking.
In your case, if equals()
is not convenient for the matching in the when()
recording, use Mockito captor with verify()
and do the check after the execution of the mock.
I don't think it's possible to differentiate between the two as generic types are arased. But there are other options.
You can couple this calls to specific order so that even if mockito cannot differentiate between them it return expected value. This is done by chaining the thenReturn
methods. First call returns responseOne()
and second call returns responseTwo()
.
when(someObject.someMethod(Matchers.<Generic<TypeA, TypeB>>any()))
.thenReturn(responseOne())
.thenReturn(responseTwo());
But this is not optimal as this test will break if your implementations changes. So here comes the second option.
You can implement custom Fake for this object. So that you can bette control how this instance behaves. For example.
class SomeObjectFake {
private final SomeResponse response;
public SomeObject(SomeResponse response) {
this.response = response;
}
public SomeResponse someMethod(Generic<TypeA, TypeB> arg) {
// Decide what to return
return response;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With