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

Mal*_*ose 4 java generics testing junit mockito

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

Map<Foo, ? extends Collection<Bar>> getValue();
Run Code Online (Sandbox Code Playgroud)

以下是我在测试中使用它的方法:

model = Mockito.mock(Model.class);
Map<Foo, List<Bar>> value = new HashMap<Foo, List<Bar>>();
Mockito.when(model.getValue()).thenReturn(value);
Run Code Online (Sandbox Code Playgroud)

它给出以下错误:

错误:找不到合适的方法 thenReturn(Map<Foo,List<Bar>>)

Ste*_*ner 8

您可以使用以下内容:

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;
  }
});
Run Code Online (Sandbox Code Playgroud)

以上可以使用lambda缩短为:

Mockito.when(model.getValue()).thenAnswer(invocationOnMock -> value)
Run Code Online (Sandbox Code Playgroud)


Pau*_*ora 5

发生此错误的原因是编译器无法保证返回的映射的值类型getValue实际上是List<Bar>. 类型的Map<Foo, ? extends Collection>意思是“某个未知Map类型的实现”。FooCollection

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

Map<Foo, Collection<Bar>> getValue();
Run Code Online (Sandbox Code Playgroud)

并使用:

model = Mockito.mock(Model.class);
Map<Foo, Collection<Bar>> value = new HashMap<Foo, Collection<Bar>>();
Mockito.when(model.getValue()).thenReturn(value);
Run Code Online (Sandbox Code Playgroud)

如果您无法更改方法的返回类型,您可以使用“捕获助手”方法进行测试:

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
Run Code Online (Sandbox Code Playgroud)

当然,这是非常有限的,因为您只能测试空地图而不知道是什么T