I've got a ThingsProvider
interface I'm trying to test with Mockito, (simplified version) defined as follows:
interface ThingsProvider {
Iterable<? extends Thing> getThings()
}
Now when I'm going to test it with Mockito, I'm doing the following (again, simplified for the question):
ThingsProvider thingsProvider = mock(ThingsProvider.class);
List<Thing> things = Arrays.asList(mock(Thing.class));
when(thingsProvider.getThings()).thenReturn(things); // PROBLEM IS HERE
Compile error message: The method thenReturn(Iterable<capture#11-of ? extends Thing>) in the type OngoingStubbing<Iterable<capture#11-of ? extends Thing>> is not applicable for the arguments (List<Thing>)
Now, purely for getting the test going, I'm changing the last line to
when(thingsProvider.getThings()).thenReturn((List)things); // HAHA take THAT generics!
... but this would clearly be bad to do in non-testing code.
My question(s):
Thing
, which is what the interface expects.On #2 - The main reason I'm not simply returning Iterable<Thing>
is that there are several different extensions where the concrete types have things buried in them that return specific subtypes, and I end up with issues like Type mismatch: cannot convert from Iterable<MagicalThing> to Iterable<Thing>
- maybe the solution is a better way to fix this issue?
For those of you less familiar with Mockito, a more pure Java version of #1 is below:
public static void main(String...args) {
List<Integer> ints = Arrays.asList(1,2,3);
blah(ints);
Foo<Number> foo1 = new Foo<Number>();
foo1.bar(ints); // This works
Foo<? extends Number> foo2 = new Foo<Number>();
foo2.bar(ints); // NO COMPILEY!
}
private static void blah(List<? extends Number> numberList) {
// something
}
public static class Foo<T> {
public Object bar(List<? extends T> tList) {
return null;
}
}
Wildcard in return type is very convenient for subclass implementations, as you've observed.
It doesn't make much difference to the callers of the method though; you may change it to Iterable<Thing>
if you'd like to; it's simpler on javadoc, at the expense of subclass implementers. Subclass can do brute cast if necessary, e.g. List<MyThing> => Iterable<Thing>
, thanks to erasure.
The reason for your problem is wildcard capture; basically every expression goes though wildcard capture first before the contextual expression is evaluated. In a method invocation
foo( arg )
arg
is always wildcard-captured first, before method applicability/overloading/inference are done. In your case, the more general type Iterable<? extends Thing>
is lost, becomes Iterable<CAP#>
.
Usually, wildcard capture does not pose any problem; but Mockito semantics is nothing usual.
The first solution is to avoid type inference; explicitly supply type arguments instead
Mockito.<Iterable<? extends Thing>>when(...
Or as Delimanolis suggested, use a target type to restrict inference (in java8+)
OngoingStubbing<Iterable<? extends Thing>> stub = when(...
It also appears that lambda inference may be helpful for this case
static <T> OngoingStubbing<T> whenX( Supplier<T> sup )
{
return Mockito.when( sup.get() );
}
whenX(thingsProvider::getThings).thenReturn(things);
// T = Iterable<? extends Thing>
And - if your interface is simple enough, just directly implement it instead of mock it :)
List<Thing> things = ...;
ThingsProvider thingsProvider = ()->things;
Refactor into two steps
OngoingStubbing<Iterable<? extends Thing>> stub = when(thingsProvider.getThings());
stub.thenReturn(things);
or use what bayou.io suggested
Mockito.<Iterable<? extends Thing>>when(thingsProvider.getThings())
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