Writing unit test is very important for better software quality. For unit tests Mockito is one of the most common choices of developers. Mockito providers different way to mock methods, like do...when and when..then. Most of the time we face the question to use when-then vs do-when. We will see all the differences in detail.
In Mockito we can mock methods in 2 different ways,
- Using when-then syntax eg: when(..).thenReturn() or when(..).thenAnswer(…)
- Using do-when syntax eg: doReturn(..).when()
DatabaseService service = Mockito.mock(DatabaseService.class);
when(service.isConnected()).thenReturn(true);
doReturn(true).when(service.isConnected())
Different methods for do-when
- doNothing()
- doReturn(Object toBeReturned)
- doReturn(Object toBeReturned, Object… toBeReturnedNext)
- doAnswer(Answer answer)
- doCallRealMethod()
- doThrow(Throwa=le… ToBeThrown)
- doThrow(Class toBeThrown)
- doThrow(Class toBeThrown, Class… toBeThrownNext)
Different methods for when-then
- thenReturn(T var1)
- thenReturn(T var1, T… var2)
- thenAnswer(Answer var1)
- then(Answer var1)
- thenCallRealMethod()
- thenThrow(Throwable… var1)
- thenThrow(Class var1)
- thenThrow(Class var1, Class… var2)
Recommended difference between thenReturn and thenAnswer in mockito
In most cases when-then is used because it provides more readability, however there are some cases, where both approaches behave differently and should be used carefully.
Return type validation for mocked object
Return type of doReturn(..) is Object whereas the return type of thenReturn(..) is as per the method type. So in case of doReturn we might get org.mockito.exceptions.misusing.WrongTypeOfReturnValue exception if incompatible return value is used. In case of thenReturn, if we use wrong value the application won't compile.
List<String> mockedList = Mockito.mock(ArrayList.class);
Mockito.when(mockedList.size()).thenReturn("test");
This will give compile time error, hence easy to fix.
List<String> mockedList = Mockito.mock(ArrayList.class);
doReturn("Test").when(mockedList).size();
This will compile but fail at runtime.
For mocked objects it is best practice to use when-then option as it provides return type checking and it is more readability. However it has drawbacks in case of spied objects and void methods.
Mocking methods of spy object
Spy objects are linked to the actual objects, where we dont have to mock all the methods. And actual method calls are made for the methods that are not mocked.
For mocked object all the methods are mocked, and there we dont need real objects.
When we are using mock, first we need to create mocked object. Then we specify what should be returned using when-then. In that case it don’t do anything with real class.
List<string> mockedList = Mockito.mock(List.class);
Mockito.when(mockedList.get(0)).thenReturn("Test");
assertEquals("Test", mockedList.get(0));When we are using spy, as it is linked to real object, when we use when(spyobject.method()).thenReturn(value), the real method on spied object is called. Even when we try to mock the behavior.
If we mock method on spied object using when-then, real method is called but mocked value is returned. This could cause exception as some fields might be null.
List<string> spiedList = Mockito.spy(List.class);
Mockito.when(mockedList.get(0)).thenReturn("Test");Real method get(0)is called on list and this throws java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
The correct way to fix this issue is to use doReturn-when
List<String> spiedList = Mockito.spy(ArrayList.class);
Mockito.doReturn("Test").when(spiedList).get(0);
assertEquals("Test", spiedList.get(0));
Note: Real method is called only for spy class objects not for spy Interfaces.
Following code works fine as we are using Interface for spy not the object.
List<String> spiedList = Mockito.spy(List.class);
Mockito.when(spiedList.get(0)).thenReturn("Test");
Mocking void method
Mocking void method is different than other methods. For mocking void method, there is no when-then option. We have to use do-when option.
Example of mocking
List<String> mockedList = Mockito.mock(List.class);
Mockito.doNothing().when(mockedList).clear();
Example of Spy
List<String> spiedList = Mockito.spy(ArrayList.class);
spiedList.add("Test");
Mockito.doNothing().when(spiedList).clear();
spiedList.clear();
assertEquals("Test", spiedList.get(0));
Fast track reading
- In Mockito method are mocked using 'when-then' or 'do-when'
- In most cases when-then is used because it provides more readability with return type validation
- Mocking methods of spy object using 'when-then' option could lead to exception hence 'do-when' option must be used
- For mocking void methods there is no 'when-then' option
- Real method of spy object is called if when-then option is used for mocking the method, however the mocked value is returned
Reference
- https://www.javadoc.io/doc/org.mockito/mockito-core/2.7.11/org/mockito/Mockito.html#doReturn(java.lang.Object)
- https://stackoverflow.com/a/11480139
- http://sangsoonam.github.io/2019/02/04/mockito-doreturn-vs-thenreturn.html