Mockito: 使用有界通配符返回类型的 Stubbing 方法

考虑下面的代码:

public class DummyClass {
public List<? extends Number> dummyMethod() {
return new ArrayList<Integer>();
}
}
public class DummyClassTest {
public void testMockitoWithGenerics() {
DummyClass dummyClass = Mockito.mock(DummyClass.class);
List<? extends Number> someList = new ArrayList<Integer>();
Mockito.when(dummyClass.dummyMethod()).thenReturn(someList); //Compiler complains about this
}
}

编译器会抱怨试图阻止 dummyMethod()行为的行。对于如何使用存根方法返回具有有界通配符的类型,有什么建议吗?

112432 次浏览

我假设您希望能够加载具有一些已知值的 someList; 这里有一种使用 Answer<T>和模板辅助方法来保持所有类型安全的方法:

@Test
public void testMockitoWithGenericsUsingAnswer()
{
DummyClass dummyClass =  Mockito.mock(DummyClass.class);


Answer<List<Integer>> answer = setupDummyListAnswer(77, 88, 99);
Mockito.when(dummyClass.dummyMethod()).thenAnswer(answer);


...
}


private <N extends Number> Answer<List<N>> setupDummyListAnswer(N... values) {
final List<N> someList = new ArrayList<N>();


someList.addAll(Arrays.asList(values));


Answer<List<N>> answer = new Answer<List<N>>() {
public List<N> answer(InvocationOnMock invocation) throws Throwable {
return someList;
}
};
return answer;
}

也可以使用非类型安全方法 返回进行此操作,

@Test
public void testMockitoWithGenerics()
{
DummyClass dummyClass = Mockito.mock(DummyClass.class);
List<? extends Number> someList = new ArrayList<Integer>();


Mockito.doReturn(someList).when(dummyClass).dummyMethod();


Assert.assertEquals(someList, dummyClass.dummyMethod());
}

作为莫基托的谷歌群组的 讨论过

虽然这比 thenAnswer简单,但是请再次注意,它不是类型安全的。如果你担心类型安全,米尔豪斯的 回答是正确的。

其他详情

明确地说,这里是观察到的编译器错误,

The method thenReturn(List<capture#1-of ? extends Number>) in the type OngoingStubbing<List<capture#1-of ? extends Number>> is not applicable for the arguments (List<capture#2-of ? extends Number>)

我相信编译器已经在 when调用期间分配了第一个通配符类型,然后无法确认 thenReturn调用中的第二个通配符类型是否相同。

看起来 thenAnswer没有遇到这个问题,因为它接受通配符类型,而 thenReturn接受非通配符类型,必须捕获这种类型。来自 Mockito 的 继续插管,

OngoingStubbing<T> thenAnswer(Answer<?> answer);
OngoingStubbing<T> thenReturn(T value);

我昨天碰到了同样的东西。来自@nondescription t1和@milhouse 的两个回答都帮助我找到了一个变通方法。我几乎使用了与@milhouse 相同的代码,只不过我使它稍微更通用一些,因为我的错误不是由 java.util.List引起的,而是由 com.google.common.base.Optional引起的。因此,我的小助手方法允许任何类型的 T,而不仅仅是 List<T>:

public static <T> Answer<T> createAnswer(final T value) {
Answer<T> dummy = new Answer<T>() {
@Override
public T answer(InvocationOnMock invocation) throws Throwable {
return value;
}
};
return dummy;
}

使用这个 helper 方法,你可以写:

Mockito.when(dummyClass.dummyMethod()).thenAnswer(createAnswer(someList));

这个函数编译得很好,并且与 thenReturn(...)方法做同样的事情。

有人知道 Java 编译器发出的错误是编译器错误还是代码真的不正确?

虽然 Marek Radonsky 提出的实用方法是可行的,但是还有另外一个选择,它甚至不需要(恕我直言,看起来很奇怪) fikovnik 建议的 lambda 表达式:

正如 这个答案对类似问题所示,您还可以使用以下命令:

BDDMockito.willReturn(someList).given(dummyClass).dummyMethod();

我在这里将 菲科夫尼克的评论转换成一个答案,以提高它的可见性,因为我认为它是使用 Java8 + 的最优雅的解决方案。

Mockito 文档建议使用 doReturn()(正如已接受的答案所建议的那样)只作为最后的手段。

相反,为了规避问题中描述的编译器错误,推荐的 Mockito when()方法可以与 thenAnswer()和 lambda (而不是助手方法)一起使用:

Mockito.when(mockedClass.mockedMethod()).thenAnswer(x -> resultList)