Mockito:存根返回带界通配符的类型的存根方法


135

考虑以下代码:

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()。关于如何使用存根方法返回带界通配符的类型的任何指针?


您可以更新代码段以显示通用类型吗?
磨坊

1
做完了 我必须删除pre和code标签,它们被剥离<?从类型声明扩展Number>。
Shikhar Mishra 2011年

Answers:


190

您也可以为此使用非类型安全方法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起来简单,但再次请注意,它不是类型安全的。如果您担心类型安全,那么米尔豪斯的答案是正确的。

额外细节

要明确的是,这是观察到的编译器错误,

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中

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

这也部分地帮助了我...但是,如果您希望返回的列表不为空,该怎么办?
ttati

除了没有空列表,您还可以执行以下操作:List <Number> someList = new ArrayList <Integer>(); someList.add(aNumber);
ttati

32

我假设您希望能够加载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;
}

17

我昨天打了同样的东西。@ nondescript1和@millhouse的两个答案都帮助我找出了解决方法。我几乎使用了与@millhouse相同的代码,除了我使它更通用外,因为我的错误不是由a引起的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编译器发出的错误是编译器错误还是代码真的不正确?


这似乎很直接,简单,而且据我所知几乎可以纠正。我不确定为什么Mockito不提供与此类似的东西....除非这样做?
vacao 2015年

14
在Java 8中,它可以缩短:Mockito.when(dummyClass.dummyMethod()).thenAnswer(x -> someList),因此不需要实用程序方法
fikovnik

1
@fikovnik“ thenAnswer”真是个了不起的发现!
borjab '16

5

我将fikovnik的评论变成答案,以使其更具可见性,因为我认为这是使用Java 8+的最优雅的解决方案。

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

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

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

尽管它没有给出任何编译时错误,但是即使我们传递带有条目的列表,返回的列表也是空的。
Venkatesh Kolla-user2742897 '19

0

尽管Marek Radonsky提出的实用方法可行,但还有另一个选择甚至不需要fikovnik建议的(恕我直言)的lambda表达式:

如对类似问题的此答案所示,您还可以使用以下内容:

BDDMockito.willReturn(someList).given(dummyClass).dummyMethod();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.