Using Mockito with Multiple Calls to the Same Method with the Same Arguments

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



Leave a reply



Submit