关于java:Mockito:使用有界Wild-Cards返回类型的存根方法

Mockito: Stubbing Methods That Return Type With Bounded Wild-Cards

考虑此代码:

1
2
3
4
5
public class DummyClass {
    public List<? extends Number> dummyMethod() {
        return new ArrayList<Integer>();
    }
}
1
2
3
4
5
6
7
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()行为的行。关于如何使用返回有界通配符的类型的存根方法,有什么建议吗?


您也可以为此使用非类型安全方法Doreturn,

1
2
3
4
5
6
7
8
9
10
@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的谷歌集团所讨论的。

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

其他详细信息

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

The method thenReturn(List) in the type OngoingStubbing> is not applicable for the arguments (List)

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

似乎thenAnswer没有遇到这个问题,因为它接受通配符类型,而thenReturn接受非通配符类型,必须捕获。从Mockito的Ongoingtubbing,

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


我假设您希望能够用一些已知的值加载someList;下面是一种方法,它使用Answer和模板化助手方法来保证所有类型的安全:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@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

1
2
3
4
5
6
7
8
9
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;
}

使用此助手方法,您可以编写:

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

这编译得很好,与thenReturn(...)方法的作用相同。

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


尽管Marek Radonsky提出的实用方法有效,但还有一种选择甚至不需要(imho奇怪的外观)lambda表达式fikovnik建议:

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

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