简体   繁体   English

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

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

Consider this code:考虑这个代码:

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
    }
}

The compiler complains about the line that's trying to stub the behavior for dummyMethod() .编译器抱怨试图存根dummyMethod()行为的dummyMethod() Any pointers on how one goes about stubbing methods that return a type with bounded wild-cards?关于如何处理返回带有有界通配符的类型的方法的任何指针?

You can also use the non-type safe method doReturn for this purpose,为此,您还可以使用非类型安全的方法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());
}

as discussed on Mockito's google group.正如在 Mockito 的 google group 上讨论的那样

While this is simpler than thenAnswer , again note that it is not type safe.虽然这比thenAnswer简单,但再次注意它不是类型安全的。 If you're concerned about type safety, millhouse's answer is correct.如果您担心类型安全,millhouse 的答案是正确的。

Additional Details额外细节

To be clear, here's the observed compiler error,需要明确的是,这是观察到的编译器错误,

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>)

I believe the compiler has assigned the first wildcard type during the when call and then cannot confirm that the second wildcard type in the thenReturn call is the same.我相信编译器在when调用期间分配了第一个通配符类型,然后无法确认thenReturn调用中的第二个通配符类型是否相同。

It looks like thenAnswer doesn't run into this issue because it accepts a wildcard type while thenReturn takes a non-wildcard type, which must be captured.看起来thenAnswer没有遇到这个问题,因为它接受通配符类型,而thenReturn接受必须捕获的非通配符类型。 From Mockito's OngoingStubbing ,从 Mockito 的OngoingStubbing

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

I'm assuming you want to be able to load up someList with some known values;我假设您希望能够使用一些已知值加载someList here's an approach that uses Answer<T> together with a templated helper method to keep everything type-safe:这是一种使用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;
}

I hit the same thing yesterday.我昨天碰到了同样的事情。 Both answers from @nondescript1 and @millhouse helped me to figure out a workaround. @nondescript1 和 @millhouse 的两个答案都帮助我找到了解决方法。 I've pretty much used the same code as @millhouse, except that I made it slightly more generic, because my error wasn't caused by a java.util.List , but the com.google.common.base.Optional .我几乎使用了与@millhouse 相同的代码,只是我使它稍微更通用,因为我的错误不是由java.util.List引起的,而是由com.google.common.base.Optional引起的。 My little helper method therefore allows for any type T and not just List<T> :因此,我的小助手方法允许使用任何类型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;
}

With this helper method you could write:使用此辅助方法,您可以编写:

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

This compiles just fine and does the same thing as the thenReturn(...) method.这编译得很好,并且与thenReturn(...)方法做同样的事情。

Does someone know if the error that the Java compiler emits is a compiler bug or if the code is really incorrect?有人知道Java编译器发出的错误是编译器错误还是代码真的不正确?

I'm turning fikovnik 's comment into an answer here to give it more visibility as I think it's the most elegant solution using Java 8+.我正在将fikovnik的评论变成这里的答案,以提高其可见性,因为我认为这是使用 Java 8+ 的最优雅的解决方案。

The Mockito documentation recommends using doReturn() (as suggested in the accepted answer) only as a last resort. Mockito 文档建议仅将doReturn() (如已接受的答案中所建议)用作最后的手段。

Instead, to circumevent the compiler error described in the question, the recommended Mockito when() approach can be used with thenAnswer() and a lambda (instead of a helper method):相反,为了规避问题中描述的编译器错误,推荐的 Mockito when()方法可以与thenAnswer()和 lambda(而不是辅助方法)一起使用:

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

Although the utility method proposed by Marek Radonsky works, there is also an other option which doesn't even require the (IMHO strange looking) lambda expression fikovnik suggested:尽管 Marek Radonsky 提出的实用方法有效,但还有另一个选项甚至不需要(恕我直言,看起来很奇怪)lambda 表达式 fikovnik 建议:

As this answer to a similar question shows, you can also use the following:至于这个答案对一个类似问题节目,你也可以使用以下命令:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM