Mockito: the return value of a method depends on another method called

In my unit test, I need to mock the interface, which among the different methods has the nextItem() and isEmpty() methods:

 public interface MyQueue { Item nextItem(); boolean isEmpty(); //other methods ... } 

My requirement for the layout is that isEmpty() should initially return false, but after nextItem() was called isEmpty() , it should return true. Thus, I am mocking the queue with one item.

  • What is the easiest way to implement such a layout with mockito?
  • Can I implement additional requirements: call nextItem() second, third, etc. will lead to a certain kind of exception?

PS I do not want to provide a full implementation of my interface for the test due to other methods in it, which leads to a difficult to understand and detailed code.

+7
source share
6 answers

You can achieve this using thenAnswer (), the documentation considers the Mockito function to be inconsistent:

Another controversial feature that was not originally included in the Mockito. We recommend that you use simple trimming only with the Return () or toThrow () parameters. These two should be enough to test / test any clean and simple code.

Here is thenAnswer:

 private boolean called = false; when(mock.nextItem()).thenAnswer(new Answer() { Object answer(InvocationOnMock invocation) { called = true; return item; } when(mock.isEmpty()).thenAnswer(new Answer() { Object answer(InvocationOnMock invocation) { return called; } }); 
+9
source

Here is a simple example:

 //given MyQueue mock = mock(MyQueue.class); given(mock.isEmpty()).willReturn(false, true); given(mock.nextItem()).willReturn(someItem); //when mock.isEmpty(); //yields false mock.nextItem(); //yields someItem mock.isEmpty(); //yields true //then InOrder inOrder = inOrder(mock); inOrder.verify(mock).isEmpty(); inOrder.verify(mock).nextItem(); inOrder.verify(mock).isEmpty(); 

willReturn(false, true) means: return false on the first call and true on the second. The InOrder object InOrder used to check the order of the call. nextItem() or remove the nextItem() call and the test will fail.

Alternatively, you can use this syntax:

 given(mock.isEmpty()). willReturn(false). willReturn(true). willThrow(SpecialException.class); 

If you need even more powerful mocking semantics, you can introduce heavy artillery - a response to the answer:

 given(mock.isEmpty()).willAnswer(new Answer<Boolean>() { private int counter = 0; @Override public Boolean answer(InvocationOnMock invocation) throws Throwable { switch(++counter) { case 1: return false; case 2: return true; default: throw new SpecialException(); } } }); 

But this can easily lead to an unreachable test code, use it with caution.

Finally, you can peek into your real object by ridiculing only the selected methods.

+5
source

You can provide some custom answer options, one of which depends on the other:

 public class NextItemAnswer implements Answer<Item> { private int invocationCount = 0; private Item item; public NextItemAnswer(Item item) { this.item = item; } public Item answer(InvocationOnMock invocation) throws Throwable { invocationCount++; return item; } public int getInvocationCount() { return invocationCount; } } public class IsEmptyAnswer implements Answer<Boolean> { private NextItemAnswer nextItemAnswer; public IsEmptyAnswer(NextItemAnswer nextItemAnswer) { this.nextItemAnswer = nextItemAnswer; } public Boolean answer(InvocationOnMock invocation) throws Throwable { return nextItemAnswer.getInvocationCount() >= 0; } } 

and then use it:

 NextItemAnswer nextItemAnswer = new NextItemAnswer(item); IsEmptyAnswer isEmptyAnswer = new IsEmptyAnswer(nextItemAnswer); when(mock.isEmpty()).thenAnswer(isEmptyAnswer); when(mock.nextItem()).thenAnswer(nextItemAnswer); 

You can customize since I have not tested this code, but the approach should be what you need.

+2
source

I understand that you explicitly wrote that you did not want to provide a full implementation of MyQueue, but to be honest, that would be the first thing I would do.

In fact, I regularly provide a “mock” of implementations of complex enough interfaces / objects to make testing easier. I am not the only one who thinks that: Spring Framework provides many mocking versions of complex objects (MockHttpServletRequest, MockHttpServletResponse, etc.), for example.

In this case, I would avoid cluttering up my test and providing this class either in a separate package, or even in production code.

A MockQueue will make your tests more readable than the other (seemingly correct) answers given here.

+2
source

You can tell mockito to respond differently to successive calls to the same mocked method using the methods described in mockito files .

 when(mock.isEmpty()) .thenReturn(false) .thenReturn(true); 

will cause the call to isEmpty() return true only on the first call, and

 when(mock.nextItem()) .thenReturn(item) .thenThrow(new NextOnEmptyQueueException()) 

will do nextItem() will return something on the first call and will throw an exception on subsequent calls.

I do not know that perhaps the result of one of these methods depends on the sequence of calls to the other. If this is really possible, I am sure it is much more complicated.

+1
source

You can make a utility and then use it wherever you want.

 public static boolean mockHasInvocation(Object mock, String methodName, Object... args) { return mockingDetails(mock).getInvocations().stream() .anyMatch(o -> o.getMethod().getName().equals(methodName) && Arrays.equals(o.getArguments(), args)); } 

Simple use:

 if(mockHasInvocation(mockObject, "methodName", "argument1", "argument2")){doSomething();} 

In this case, you do not need additional variables, and this is more of a "Mockito style."

+1
source

All Articles