Reputation: 6562
Is there a way to have a stubbed method return different objects on subsequent invocations? I'd like to do this to test nondeterminate responses from an ExecutorCompletionService
. i.e. to test that irrespective of the return order of the methods, the outcome remains constant.
The code I'm looking to test looks something like this.
// 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 (...) { }
}
Upvotes: 520
Views: 515733
Reputation: 79807
How about
when(method-call).thenReturn(value1, value2, value3);
You can put as many arguments as you like in the brackets of thenReturn, provided they're all the correct type. The first value will be returned the first time the method is called, then the second answer, and so on. The last value will be returned repeatedly once all the other values are used up.
Upvotes: 982
Reputation: 27958
You can use a LinkedList
and an Answer
. Eg
MyService mock = mock(MyService.class);
LinkedList<String> results = new LinkedList<>(List.of("A", "B", "C"));
when(mock.doSomething(any())).thenAnswer(invocation -> results.removeFirst());
Or perhaps
LinkedList<Answer<String>> answers = new LinkedList<>();
answers.add(invocation -> "A");
answers.add(invocation -> "B");
answers.add(invocation -> { throw new Exception(); });
when(mock.doSomething(any())).thenAnswer(invocation -> results.removeFirst().apply(invocation));
Upvotes: 3
Reputation: 10890
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);
Option #1
Suppose we have 2 args, and check the size of the 2nd (list) arg:
...
when(yourMock.yourMethod(any(), anyList()))
.thenAnswer(args -> ((List) args.getArgument(1)).size() < 2
? 1
: 3);
args are Objects, so we have to cast an arg to our type. I cast ^^^ to (List)
in my case.
Option #2 (BDD)
...
given(yourMock.yourMethod(any(), anyList()))
.willAnswer(args -> ((List) args.getArgument(1)).size() < 2
? 1
: 3);
Upvotes: 59
Reputation: 2571
This might be basic/obvious, but if like me you are trying to mock multiple calls for a method that is called unknown number of times per call to method to be tested, for example:
public String method(String testArg) {
//...
while(condition) {
someValue = someBean.nestedMethod(); // This is called unknown number of times
//...
}
//...
}
You can do something like:
@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();
}
Upvotes: 1
Reputation: 121
If you have a dynamic list of values you can use AdditionalAnswers.returnsElementsOf
:
import org.mockito.AdditionalAnswers;
when(mock.method()).thenAnswer(AdditionalAnswers.returnsElementsOf(myListOfValues));
Upvotes: 8
Reputation: 1390
Almost all of the calls are chainable:
doReturn(null).doReturn(anotherInstance).when(mock).method();
Upvotes: 95
Reputation: 81
This is not directly related to the question. But wanted to put this in the same chain.
If trying to verify the same method call with multiple arguments, you can use the below times feature by Mockito. You don't need it if you are not verifying.
Mockito.verify(method, times(n)).methoscall();
Here is 'n' is the number of times the mock is invoked.
Upvotes: 2
Reputation: 1166
Here is working example in BDD style which is pretty simple and clear
given(carRepository.findByName(any(String.class))).willReturn(Optional.empty()).willReturn(Optional.of(MockData.createCarEntity()));
Upvotes: 1
Reputation: 753
Related to @[Igor Nikolaev]'s answer from 8 years ago, using an Answer
can be simplified somewhat using a lambda expression available in Java 8.
when(someMock.someMethod()).thenAnswer(invocation -> {
doStuff();
return;
});
or more simply:
when(someMock.someMethod()).thenAnswer(invocation -> doStuff());
Upvotes: 4
Reputation: 4697
You can do that using the thenAnswer
method (when chaining with when
):
when(someMock.someMethod()).thenAnswer(new Answer() {
private int count = 0;
public Object answer(InvocationOnMock invocation) {
if (count++ == 1)
return 1;
return 2;
}
});
Or using the equivalent, static doAnswer
method:
doAnswer(new Answer() {
private int count = 0;
public Object answer(InvocationOnMock invocation) {
if (count++ == 1)
return 1;
return 2;
}
}).when(someMock).someMethod();
Upvotes: 351
Reputation: 6528
As previously pointed out almost all of the calls are chainable.
So you could call
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();
More info in Mockito's Documenation.
Upvotes: 257
Reputation: 78
I've implemented a MultipleAnswer
class that helps me to stub different answers in every call. Here the piece of code:
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);
}
}
Upvotes: 5
Reputation: 163
Following can be used as a common method to return different arguments on different method calls. Only thing we need to do is we need to pass an array with order in which objects should be retrieved in each call.
@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;
}
}
}
Ex. getAnswerForSubsequentCalls(mock1, mock3, mock2);
will return mock1 object on first call, mock3 object on second call and mock2 object on third call.
Should be used like when(something()).doAnswer(getAnswerForSubsequentCalls(mock1, mock3, mock2));
This is almost similar to when(something()).thenReturn(mock1, mock3, mock2);
Upvotes: 1