Mockito: stub返回有界通配符类型的方法



考虑以下代码:

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()行为的行。任何指针如何去stub方法返回一个有界通配符的类型?

您也可以使用非类型安全的方法doReturn来实现此目的,

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

在Mockito的google群组中讨论过。

虽然这比thenAnswer简单,但再次注意它不是类型安全的。如果你关心类型安全,millhouse的答案是正确的。

额外的细节这里是观察到的编译器错误,

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接受非通配符类型,必须捕获。From Mockito's OngoingStubbing

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

我假设你想要能够加载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;
}

我昨天碰到了同样的事情。@nondescript1和@millhouse的回答都帮助我找到了一个解决办法。我几乎使用了与@millhouse相同的代码,除了我使它稍微更通用,因为我的错误不是由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;
}

使用这个辅助方法你可以这样写:

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

这个编译得很好,和thenReturn(...)方法一样。

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

我在这里把fikovnik的评论变成一个答案,让它更可见,因为我认为这是使用Java 8+的最优雅的解决方案。

Mockito文档建议仅在万不得已时使用doReturn()(如已接受的答案所建议的那样)。

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

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

虽然Marek Radonsky提出的实用方法有效,但还有另一种选择,甚至不需要fikovnik建议的(IMHO奇怪的)lambda表达式:

正如对类似问题的回答所示,你还可以使用以下语句:

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

最新更新