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

时间:2011-09-09 18:59:18

标签: java unit-testing generics mockito bounded-wildcard

考虑以下代码:

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

6 个答案:

答案 0 :(得分:153)

您也可以使用非类型安全方法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谷歌小组的discussed

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

其他详细信息

要清楚,这是观察到的编译器错误,

  

方法然后返回(List&lt; capture#1-of?extends Number&gt;)类型为OngoingStubbing&lt; List&lt; capture#1-of? extends Number&gt;&gt;不适用于参数(List&lt; capture#2-of?extends Number&gt;)

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

看起来thenAnswer不会遇到此问题,因为它接受通配符类型,而thenReturn采用非通配符类型,必须捕获该类型。来自Mockito的OngoingStubbing

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

答案 1 :(得分:29)

我假设您希望能够使用某些已知值加载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;
}

答案 2 :(得分:13)

我昨天打了同样的话。 @ 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编译器发出的错误是编译错误还是代码错误?

答案 3 :(得分:1)

我将fikovnik's comment用作答案,以使其更具可见性,因为我认为这是使用Java 8+的最优雅的解决方案。

Mockito documentation建议仅将doReturn()(如接受的答案中建议的那样)用作最后的选择。

相反,要避免问题中描述的编译器错误,建议的Mockito when()方法可与thenAnswer()和lambda(而不是辅助方法)一起使用:

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

答案 4 :(得分:0)

尽管Marek Radonsky提出的实用方法有效,但还有一个其他选项甚至不需要(IMHO奇怪的)lambda表达式fikovnik建议:

对于类似问题的this answer,您还可以使用以下内容:

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

答案 5 :(得分:0)

Mockito.when(dummyClass.dummyMethod()).thenReturn(someList);
    //    `dummyMethod()` should return variable with same type as `someList`