Java泛型-方法不适用于Mockito生成的存根

创造

我有一个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!

...但是在非测试代码中这样做显然是不好的。

我的问题:

  1. 为什么这是一个错误?我显然返回的是extend对象Thing,这是接口所期望的。
  2. 有解决这个问题的更好方法吗?也许以不同的方式定义我的界面?到目前为止,我还没有在测试之外遇到这个问题。

在#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;
    }
}
ZhongYu

如您所见,返回类型中的通配符对于子类实现非常方便。

但是,它与方法的调用者并没有太大区别;您可以根据需要将其更改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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章