我有一个ThingsProvider
要尝试使用Mockito(简化版)测试的界面,定义如下:
interface ThingsProvider {
Iterable<? extends Thing> getThings()
}
现在,当我要使用Mockito对其进行测试时,我正在执行以下操作(再次简化该问题):
ThingsProvider thingsProvider = mock(ThingsProvider.class);
List<Thing> things = Arrays.asList(mock(Thing.class));
when(thingsProvider.getThings()).thenReturn(things); // PROBLEM IS HERE
编译错误信息: 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>)
现在,纯粹是为了进行测试,我将最后一行更改为
when(thingsProvider.getThings()).thenReturn((List)things); // HAHA take THAT generics!
...但是在非测试代码中这样做显然是不好的。
我的问题:
Thing
,这是接口所期望的。在#2上-我不简单返回的主要原因Iterable<Thing>
是,有几种不同的扩展,其中具体类型中埋藏着返回特定子类型的东西,而我最终Type mismatch: cannot convert from Iterable<MagicalThing> to Iterable<Thing>
遇到了类似的问题-也许解决方案是一种更好的解决方案解决这个问题?
对于那些不太熟悉Mockito的人,下面是更纯粹的Java版本#1:
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;
}
}
如您所见,返回类型中的通配符对于子类实现非常方便。
但是,它与方法的调用者并没有太大区别;您可以根据需要将其更改Iterable<Thing>
为;它在javadoc上更简单,但以子类实现者为代价。子类可以在必要时进行暴力破解,例如List<MyThing> => Iterable<Thing>
,由于擦除。
您遇到问题的原因是通配符捕获;基本上,每个表达式都会先经过通配符捕获,然后再评估上下文表达式。在方法调用中
foo( arg )
arg
在方法适用性/重载/推理完成之前,始终始终先捕获通配符。在您的情况下,更通用的类型Iterable<? extends Thing>
丢失了,变为Iterable<CAP#>
。
通常,通配符捕获不会造成任何问题。但是Mockito语义并不常见。
第一种解决方案是避免类型推断。显式提供类型参数
Mockito.<Iterable<? extends Thing>>when(...
或按照Delimanolis的建议,使用目标类型限制推理(在Java8 +中)
OngoingStubbing<Iterable<? extends Thing>> stub = when(...
看来lambda推断可能对这种情况有帮助
static <T> OngoingStubbing<T> whenX( Supplier<T> sup )
{
return Mockito.when( sup.get() );
}
whenX(thingsProvider::getThings).thenReturn(things);
// T = Iterable<? extends Thing>
并且-如果您的界面足够简单,只需直接实现它即可,而不是模拟它:)
List<Thing> things = ...;
ThingsProvider thingsProvider = ()->things;
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句