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

时间:2013-06-27 12:54:49

标签: java generics testing junit mockito

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

Map<Foo, ? extends Collection<Bar>> getValue();

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

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

它出现以下错误:

  

错误:找不到thenReturn(Map<Foo,List<Bar>>)

的合适方法

3 个答案:

答案 0 :(得分:6)

您可以使用以下内容:

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;
  }
});

可以使用lambda缩短上面的内容:

Mockito.when(model.getValue()).thenAnswer(invocationOnMock -> value)

答案 1 :(得分:4)

发生此错误是因为编译器无法保证getValue返回的映射的值类型实际上是List<Bar>。类型Map<Foo, ? extends Collection>表示“Map Foo Collection未知类型实现Map<Foo, Collection<Bar>> getValue(); ”。

这是一个很好的例子,说明为什么不鼓励在返回类型中使用通配符,因为它们通常会通过隐藏有关返回内容的泛型类型信息来禁止调用者(相反,鼓励在方法参数中使用通配符,因为它会使事情 更方便调用者)。如果可能,我建议删除通配符:

model = Mockito.mock(Model.class);
Map<Foo, Collection<Bar>> value = new HashMap<Foo, Collection<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

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

T

当然这是非常有限的,因为你只能在不知道{{1}}是什么的情况下测试一张空地图。

答案 2 :(得分:1)

如果您不想编写辅助函数,使用doReturn ... when也可以,就像这样(尽管它不是类型安全的):

Mockito.doReturn(value).when(model).getValue();
public class Test {
    interface Model {
        Map<Foo, ? extends Collection<Bar>> getValue();
    }
    class Bar {}
    class Foo {}

    public static void main(String[] args) {
        Model model = Mockito.mock(Model.class);
        Map<Foo, List<Bar>> value = new HashMap<Foo, List<Bar>>();

//      when(model.getValue()).thenReturn(value); // won't compile
        doReturn(value).when(model).getValue();

        System.out.println(model.getValue());
    }
}