使用Mockito多次调用具有相同参数的相同方法

是否有一种方法可以让存根方法在后续调用时返回不同的对象?我想这样做来测试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 (...) { }
}
383089 次浏览

你可以使用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();

如何

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

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

几乎所有的调用都是可链的:

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

作为之前指出过,几乎所有的调用都是可链的。

所以你可以打电话

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

更多信息在5的Documenation

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

@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;
}
}
}
< p >交货。getAnswerForSubsequentCalls(mock1, mock3, mock2);将在第一次调用时返回mock1对象,第二次调用时返回mock3对象,第三次调用时返回mock2对象。 应该像when(something()).doAnswer(getAnswerForSubsequentCalls(mock1, mock3, mock2)); 这几乎类似于when(something()).thenReturn(mock1, mock3, mock2);

我实现了一个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);
}
}

与@[Igor Nikolaev] 8年前的回答有关,使用Answer可以使用Java 8中可用的lambda表达式进行简化。

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

或者更简单地说:

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

BDD风格:

import static org.mockito.BDDMockito.given;
...


given(yourMock.yourMethod()).willReturn(1, 2, 3);

经典的风格:

import static org.mockito.Mockito.when;
...


when(yourMock.yourMethod()).thenReturn(1, 2, 3);

明确的风格:

        ...


when(yourMock.yourMethod())
.thenReturn(1)
.thenReturn(2)
.thenReturn(3);

取决于参数

假设我们有2个参数,检查第2个(列表)参数的大小:

        ...


when(yourMock.yourMethod(any(), anyList()))
.thenAnswer(args -> ((List) args.getArgument(1)).size() < 2
? 1
: 3);

arg是对象,所以我们必须将arg转换为我们的类型。在我的例子中,我将^^^转换为(List)

BDD

        ...


given(yourMock.yourMethod(any(), anyList()))
.willAnswer(args -> ((List) args.getArgument(1)).size() < 2
? 1
: 3);

doReturn(value1, value2, value3)。When(方法调用)

这里是BDD风格的工作示例,非常简单和清晰

given(carRepository.findByName(any(String.class))).willReturn(Optional.empty()).willReturn(Optional.of(MockData.createCarEntity()));

你可以使用LinkedListAnswer。如

MyService mock = mock(MyService.class);
LinkedList<String> results = new LinkedList<>(List.of("A", "B", "C"));
when(mock.doSomething(any())).thenAnswer(invocation -> results.removeFirst());

这和问题没有直接关系。但我想把这个放在同一个链上。

如果试图用多个参数验证同一个方法调用,可以使用Mockito的下面的times特性。如果你不验证,你就不需要它。

5。验证((n)方法,倍).methoscall ();

这里的“n”是调用mock的次数。

如果你有一个动态的值列表,你可以使用AdditionalAnswers.returnsElementsOf:

import org.mockito.AdditionalAnswers;


when(mock.method()).thenAnswer(AdditionalAnswers.returnsElementsOf(myListOfValues));

这可能是基本的/明显的,但如果像我一样,你试图模拟一个方法的多次调用,每次调用要测试的方法被调用未知次数,例如:

public String method(String testArg) {
//...
while(condition) {
someValue = someBean.nestedMethod(); // This is called unknown number of times
//...
}
//...
}

你可以这样做:

@Test
public void testMethod() {
mockNestedMethodForValue("value1");
assertEquals(method("arg"), "expected1");
mockNestedMethodForValue("value2");
assertEquals(method("arg"), "expected2");
mockNestedMethodForValue("value3");
assertEquals(method("arg"), "expected3");
}


private void mockNestedMethodForValue(String value) {
doReturn(value).when(someBeanMock).nestedMethod();
}