将Mockito与具有相同参数的多次调用相同方法一起使用


289

有一种方法可以使存根方法在后续调用中返回不同的对象吗?我想这样做是为了测试来自的不确定响应ExecutorCompletionService。即,不管方法的返回顺序如何进行测试,结果都保持恒定。

我要测试的代码看起来像这样。

// Create an completion service so we can group these tasks together
ExecutorCompletionService<T> completionService =
        new ExecutorCompletionService<T>(service);

// Add all these tasks to the completion service
for (Callable<T> t : ts)
    completionService.submit(request);

// As an when each call finished, add it to the response set.
for (int i = 0; i < calls.size(); i ++) {
    try {
        T t = completionService.take().get();
        // do some stuff that I want to test
    } catch (...) { }        
}

Answers:


254

您可以使用thenAnswer方法来做到这一点(与链接时when):

when(someMock.someMethod()).thenAnswer(new Answer() {
    private int count = 0;

    public Object answer(InvocationOnMock invocation) {
        if (count++ == 1)
            return 1;

        return 2;
    }
});

或使用等效的静态doAnswer方法:

doAnswer(new Answer() {
    private int count = 0;

    public Object answer(InvocationOnMock invocation) {
        if (count++ == 1)
            return 1;

        return 2;
    }
}).when(someMock).someMethod();

634

怎么样

when( method-call ).thenReturn( value1, value2, value3 );

您可以在thenReturn的括号中放入任意数量的参数,只要它们都是正确的类型即可。第一次调用该方法时将返回第一个值,然后将返回第二个答案,依此类推。一旦所有其他值用完,将重复返回最后一个值。


4
这将适用于模拟,但不适用于间谍。如果需要防止调用原始方法,则需要doAnswer(...)。when(someSpy).someMethod(...)。
Yuri 2014年

6
@Yuri-不完全是。在您提到的情况下,您不需要doAnswer或编写一个Answer。您可以使用doReturn(...).when(someSpy).someMethod(...)。假设Emma对模拟感兴趣,而不是对间谍感兴趣,这似乎是合理的,但是我想我可以在答案中添加一些内容来说明这一点。感谢您的评论。
·伊本·卡里姆

@DawoodibnKareem可以说,对于第一个调用我想返回一个值,对于第二个调用我想抛出一个异常。如何才能做到这一点?
Rito

@Rito请阅读Volodymyr的答案或Raystorm的答案。他们都涵盖了这种情况。
达伍德·伊本·卡里姆

如此光荣的答案。
wild_nothing

151

如前所述几乎所有调用都是可链接的。

所以你可以打电话

when(mock.method()).thenReturn(foo).thenReturn(bar).thenThrow(new Exception("test"));

//OR if you're mocking a void method and/or using spy instead of mock

doReturn(foo).doReturn(bar).doThrow(new Exception("Test").when(mock).method();

有关Mockito的文档资料的更多信息。


3
非常有帮助!mock.method在此示例中,第四次调用将发生什么?我想要类似的东西,第一次返回foo,但其余所有返回bar。
javaPlease42 '16

6
模拟中的每个其他调用都会返回最后的'thenReturn'或最后的'thenThrow'-非常有用
Francois Lacoursiere

感谢您的出色而简单的说明。直到现在都还不知道。我一直在努力寻找如何在两个相同的通话中获得两个不同的结果。节省我很多时间。
CharlesC

68

您甚至可以doReturn()像这样链接方法调用

doReturn(null).doReturn(anotherInstance).when(mock).method();

可爱不是吗:)


4

我实现了一个MultipleAnswer类,可以帮助我在每个电话中添加不同的答案。这是一段代码:

private final class MultipleAnswer<T> implements Answer<T> {

    private final ArrayList<Answer<T>> mAnswers;

    MultipleAnswer(Answer<T>... answer) {
        mAnswers = new ArrayList<>();
        mAnswers.addAll(Arrays.asList(answer));
    }

    @Override
    public T answer(InvocationOnMock invocation) throws Throwable {
        return mAnswers.remove(0).answer(invocation);
    }
}

1
您能否以简短,简单且易读的方式初始化该对象?
USR-本地ΕΨΗΕΛΩΝ

1

以下可以用作在不同方法调用上返回不同参数的通用方法。我们唯一需要做的就是传递一个带有顺序的数组,该顺序应在每次调用中检索对象。

@SafeVarargs
public static <Mock> Answer<Mock> getAnswerForSubsequentCalls(final Mock... mockArr) {
    return new Answer<Mock>() {
       private int count=0, size=mockArr.length;
       public Mock answer(InvocationOnMock invocation) throws throwable {
           Mock mock = null;
           for(; count<size && mock==null; count++){
                mock = mockArr[count];
           }

           return mock;    
       } 
    }
}

例如 getAnswerForSubsequentCalls(mock1, mock3, mock2);将在第一次调用时返回模拟对象1,在第二次调用时返回模拟对象3,在第三次调用时返回模拟对象2。应该像when(something()).doAnswer(getAnswerForSubsequentCalls(mock1, mock3, mock2)); 这样使用when(something()).thenReturn(mock1, mock3, mock2);


1

与8年前@ [Igor Nikolaev]的答案有关,使用Java 8中可用Answerlambda表达式可以稍微简化使用。

when(someMock.someMethod()).thenAnswer(invocation -> {
    doStuff();
    return;
});

或更简单地说:

when(someMock.someMethod()).thenAnswer(invocation -> doStuff());

1

BDD样式:

import static org.mockito.BDDMockito.*;
...
    @Test
    void submit() {
        given(yourMock.yourMethod()).willReturn(1, 2, 3);

1

doReturn(value1,value2,value3)。when(method-call)

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.