Java 模拟具有泛型(?extends Collection)返回类型的方法时存在问题

Java 模拟具有泛型(?extends Collection)返回类型的方法时存在问题,java,generics,testing,junit,mockito,Java,Generics,Testing,Junit,Mockito,我在使用mockito模拟方法时遇到问题,如下所示: Map<Foo, ? extends Collection<Bar>> getValue(); model = Mockito.mock(Model.class); final Map<Foo, List<Bar>> value = new HashMap<Foo, List<Bar>>(); Mockito.when(model.getValue()).thenAn

我在使用mockito模拟方法时遇到问题,如下所示:

Map<Foo, ? extends Collection<Bar>> getValue();
model = Mockito.mock(Model.class);
final Map<Foo, List<Bar>> value = new HashMap<Foo, List<Bar>>();

Mockito.when(model.getValue()).thenAnswer(new Answer<Map<Foo, List<Bar>>> () {
  public Map<Foo, List<Bar>> answer(InvocationOnMock invocation) throws Throwable {
    return value;
  }
});
Map getValue();
以下是我在测试中如何使用它:

model = Mockito.mock(Model.class);
Map<Foo, List<Bar>> value = new HashMap<Foo, List<Bar>>();
Mockito.when(model.getValue()).thenReturn(value);
private <T extends Collection<Bar>> test(Map<Foo, T> actual) {
    Map<Foo, T> expected = new HashMap<Foo, T>();
    Mockito.when(actual).thenReturn(expected);
}

...

model = Mockito.mock(Model.class);
test(model.getValue()); // T is resolved to wildcard capture
model=Mockito.mock(model.class);
Map value=newhashmap();
Mockito.when(model.getValue()).thenReturn(value);
它给出了以下错误:

错误:找不到适用于
thenReturn(Map)


发生此错误是因为编译器无法保证
getValue
返回的映射的值类型实际上是
List
。类型
Map
表示“将
Foo
s的
Map
映射到实现
集合的某个未知类型”

这是一个很好的例子,说明了为什么不鼓励在返回类型中使用通配符,因为通配符通常通过隐藏有关返回内容的泛型类型信息来抑制调用者(相反,鼓励在方法参数中使用通配符,因为它使调用者更容易)。如果可能的话,我建议去掉通配符:

Map<Foo, Collection<Bar>> getValue();
Map getValue();
和使用:

model = Mockito.mock(Model.class);
Map<Foo, Collection<Bar>> value = new HashMap<Foo, Collection<Bar>>();
Mockito.when(model.getValue()).thenReturn(value);
model=Mockito.mock(model.class);
Map value=newhashmap();
Mockito.when(model.getValue()).thenReturn(value);
如果无法更改该方法的返回类型,则可以使用“capture helper”方法进行测试:

model = Mockito.mock(Model.class);
Map<Foo, List<Bar>> value = new HashMap<Foo, List<Bar>>();
Mockito.when(model.getValue()).thenReturn(value);
private <T extends Collection<Bar>> test(Map<Foo, T> actual) {
    Map<Foo, T> expected = new HashMap<Foo, T>();
    Mockito.when(actual).thenReturn(expected);
}

...

model = Mockito.mock(Model.class);
test(model.getValue()); // T is resolved to wildcard capture
专用测试(实际映射){
Map expected=新的HashMap();
Mockito.when(实际),然后return(预期);
}
...
model=Mockito.mock(model.class);
测试(model.getValue());//T解析为通配符捕获

当然,这是非常有限的,因为您只能在不知道
T
是什么的情况下测试空映射。

您可以使用以下方法:

Map<Foo, ? extends Collection<Bar>> getValue();
model = Mockito.mock(Model.class);
final Map<Foo, List<Bar>> value = new HashMap<Foo, List<Bar>>();

Mockito.when(model.getValue()).thenAnswer(new Answer<Map<Foo, List<Bar>>> () {
  public Map<Foo, List<Bar>> answer(InvocationOnMock invocation) throws Throwable {
    return value;
  }
});

如果您不想编写助手函数,请在
工作时使用
doReturn
,如下所示(尽管它不是类型安全的):

公共类测试{
界面模型{
映射getValue();
}
类条{}
类Foo{}
公共静态void main(字符串[]args){
Model Model=Mockito.mock(Model.class);
Map value=newhashmap();
//当(model.getValue()).thenReturn(value);//不编译时
doReturn(value).when(model).getValue();
System.out.println(model.getValue());
}
}

模型
通用的,类似于
模型
?即使公认的解决方案更简洁,这一个也解释了问题的“原因”,这是有经验的开发人员对自己做的第一个问题。这是可行的,但在这个实现中有区别,对吗?不管怎样,最初的方法调用该方法,这个建议的方法只在测试中真正调用时才调用模拟方法,还是相反?