Using Mockito with multiple calls to the same method with the same arguments
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();
Mockito mock same method calls with different collection-arguments
Your second call - Mockito.when(c.run(b)).thenReturn("b");
is overruling our first call so Mockito will therefore always return "b".
If you need multiple answers from the same call, you can use the varags variant:
when(c.run(anyCollection())).thenReturn("a", "b");
Now the first call to the controller's run method will return "a" and all subsequent calls will return "b". You can provide as many return results as you want and the last one will be repeated from then on as the answer.
Calling Mockito.when multiple times on same object?
One of the problems with Mockito.when
is that the argument you pass to it is the expression that you're trying to stub. So when you use Mockito.when
twice for the same method call, the second time you use it, you'll actually get the behaviour that you stubbed the first time.
I actually recommend NOT using Mockito.when
. There are many traps that you can fall into when you use it - quite a few cases when you need some other syntax instead. The "safer" alternative syntax is the "do" family of Mockito methods.
doReturn(value).when(mock).method(arguments ...);
doThrow(exception).when(mock).method(arguments ...);
doAnswer(answer).when(mock).method(arguments ...);
So in your case, you want
doThrow(new BadSqlGrammarException(??, ??, ??)).when(accountMapper).createBadGrammarException();
If you are starting out with Mockito, then I recommend that you learn to use the "do" family. They're the only way to mock void methods, and the Mockito documentation specifically mentions that. But they can be used whenever Mockito.when
can be used. So if you use the "do" family, you'll end up with more consistency in your tests, and less of a learning curve.
For more information about the cases when you must use the "do" family, see my answer on Forming Mockito "grammars"
Mockito: multiple calls to the same method
Your stub should work as you want it. From Mockito doc:
Once stubbed, the method will always return stubbed value regardless
of how many times it is called.
Mock same method with different parameters
One way could be to avoid being too restrictive on your arguments in order to provide all the expected results with only one thenReturn
call.
For example let's say that I want to mock this method:
public String foo(String firstArgument, Object obj) {
return "Something";
}
You could then mock it by providing as many results as you want like below:
// Mock the call of foo of any String to provide 3 results
when(mock.foo(anyString(), anyObject())).thenReturn("val1", "val2", "val3");
Calls to foo
with any parameters will provide respectively "val1
", "val2
", then any subsequent calls will provide "val3
".
In case you do care about passed values but don't want to depend on call sequence you can use thenAnswer
to provide an answer that matches with the second argument like you currently do but with 3 different thenReturn
.
Assuming that you have overridden the method equals(Object o)
.
when(mock.foo(anyString(), anyObject())).thenAnswer(
invocation -> {
Object argument = invocation.getArguments()[1];
if (argument.equals(new ARequest(1, "A"))) {
return new AResponse(1, "passed");
} else if (argument.equals(new ARequest(2, "2A"))) {
return new AResponse(2, "passed");
} else if (argument.equals(new BRequest(1, "B"))) {
return new BResponse(112, "passed");
}
throw new InvalidUseOfMatchersException(
String.format("Argument %s does not match", argument)
);
}
);
Or simply, using the methods anyString
and eq
as argument marchers.
Assuming that you have overridden the method equals(Object o)
.
when(service.foo(anyString(), eq(new ARequest(1, "A"))))
.thenReturn(new AResponse(1, "passed"));
when(service.foo(anyString(), eq(new ARequest(2, "2A"))))
.thenReturn(new AResponse(2, "passed"));
when(service.foo(anyString(), eq(new BRequest(1, "B"))))
.thenReturn(new BResponse(112, "passed"));
How can I get two method calls on the same Mock to return different values?
You can chain thenReturn
, so that subsequent calls to the mock return different things:
public class GroceryServiceTest {
@Test
public void checkout() {
GroceryService.GroceryListDao groceryListDao = mock(GroceryService.GroceryListDao.class);
GroceryService groceryService = new GroceryService(groceryListDao);
List<GroceryService.Cereal> cereal = new ArrayList<>();
GroceryService.Cereal x = new GroceryService.Cereal();
cereal.add(x);
// first return a list with one item, then an empty list
when(groceryListDao.getList(anyString(), anyString())).thenReturn(cereal).thenReturn(Collections.emptyList());
groceryService.checkout("10", "A5ALV350IIXL");
verify(groceryListDao, times(1)).total();
}
}
This is not a perfect test, as the mock would return an empty list without an intervening call to total
.
You can simulate the semantics of your DAO like this:
public class GroceryServiceTest {
@Test
public void checkout() {
GroceryService.GroceryListDao groceryListDao = mock(GroceryService.GroceryListDao.class);
GroceryService groceryService = new GroceryService(groceryListDao);
List<GroceryService.Cereal> cereal = new ArrayList<>();
AtomicBoolean totalCalled = new AtomicBoolean(false);
GroceryService.Cereal x = new GroceryService.Cereal();
cereal.add(x);
when(groceryListDao.getList(anyString(), anyString())).thenAnswer(new Answer<List<GroceryService.Cereal>>() {
@Override
public List<GroceryService.Cereal> answer(InvocationOnMock invocationOnMock) throws Throwable {
if (totalCalled.get()) {
return Collections.emptyList();
} else {
return cereal;
}
}
});
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
totalCalled.set(true);
return null;
}
}).when(groceryListDao).total();
groceryService.checkout("10", "A5ALV350IIXL");
verify(groceryListDao, times(1)).total();
}
}
For completeness, here's the GroceryService
code:
import java.util.List;
public class GroceryService {
final GroceryService.GroceryListDao groceryListDao;
public GroceryService(GroceryService.GroceryListDao groceryListDao) {
this.groceryListDao = groceryListDao;
}
interface GroceryListDao {
List<Cereal> getList(String maxItems, String code);
void total();
}
static class Cereal {}
public void checkout(String maxItems, String code){
List<Cereal> groceryList;
groceryList = groceryListDao.getList(maxItems, code);
while (!groceryList.isEmpty()){
groceryListDao.total();
//other logic
groceryList = groceryListDao.getList(maxItems, code);
}
}
}
How to mock the same method multiple times use mockito
Normally when the set-up is more complicated, the doAnswer
strategy would be used:
Mockito.doAnswer(new Answer<Float>() {
@Override
public Float answer(InvocationOnMock invocation) throws Throwable {
String argument = (String)invocation.getArgument(0);
if(supportedDates.contains(argument)){
return 100.00f;
}else{
return null;
}
}
}).when(accountingDao.sumOfDay(any(String.class)));
So you basically catch the input param and then decide based on its value what should be returned dynamically.
How to call another method with same arguments using `when/then` mockito
You can use thenAnswer
, where you can write a lambda that takes an Answer
object as parameter where you can extract the arguments of the call to addDate
:
when(foo.addDate(eq("myString"), any()))
.thenAnswer(answer ->
foo.addString(answer.getArgument(0)));
Have a look at the docs here.
ArgumentMatchers for multiple calls on the same mock using Mockito 2.7.5
Given that ordering (i.e. when(...).thenReturn(true, false)
) won't suffice for you you'll have to use thenAnswer
to test the given argument and return a value accordingly.
For example:
when(generalUtilmock.isObjectEmpty(ArgumentMatchers.<List<License>>any()))).thenAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
// if args contains a List<License> then return false
// if args contains a List<AccountValidationResponseDTO> then return true
}
});
Related Topics
What Is Recommended Way for Spawning Threads from a Servlet in Tomcat
Sorting Using Comparator- Descending Order (User Defined Classes)
Override Java System.Currenttimemillis for Testing Time Sensitive Code
Jre 1.7 - Java Version - Returns: Java/Lang/Noclassdeffounderror: Java/Lang/Object
Using a Custom Truststore in Java as Well as the Default One
Deep Clone Utility Recommendation
How to Obtain Localdatetime from Temporalaccessor When Parsing Localdatetime (Java 8)
How to Create Custom Exceptions in Java
How to Calculate Elapsed Time from Now with Joda-Time
Create New Object from a String in Java
Create Java Console Inside a Gui Panel
Purpose of a Constructor in Java
Eclipse Error: "The Import Xxx Cannot Be Resolved"