Three Minutes to Mockito - A Primer on Mocking and verifying in Java
Let us create a class SearchWrapper
in Java which does linear or binary search based on strategy.
class SearchWrapper {
BinarySearch binarySearch;
LinearSearch linearSearch;
SearchWrapper(binarySearch, linearSearch){
this.linearSearch = linearSearch;
this.binarySearch = binarySearch;
}
public boolean search(int[] array, int number, String strategy){
if(strategy.equals("LINEAR")) return linearSearch.search(array, number);
return binarySearch.search(array, number);
}
}
The above code takes both linear and binary search during creation but while calling search it uses one of them based on strategy.
Let us test it,
public class SearchWrapperTest {
Searchwrapper searchWrapper;
@BeforeEach
public void setup() {
searchWrapper = new SearchWrapper(new BinarySearch(), new LinearSearch());
}
@Test
public void testShouldLinearSearch(){
boolean expected = searchWrapper.search(new int[]{1,2,3} , 1, "LINEAR");
Assertions.assertTrue(expected);
}
}
But there are 2 problems in the above code,
You will never be able to actually test if linear search is being called.
SearchWrapperTest
will also fail if something changes in linear search. This might not be a big problem if you see now, but imagine in a big codebase with hundreds and thousands of file and every single change in one file will have domino effect on other tests. So, it is always recommended that only that particular class behaviour is being tested.
This is where Mock
comes into picture, it basically mocks(imitates) the behaviour as the name suggests. The following is the way to mock a class.
linearSearch = Mockito.mock(LinearSearch.class);D
Syntax: Mockito.mock(className)
But since it is a mock, it does not know what to return while the search
method is called. To instruct it what to do, we can use
Mockito.when(linearSearch.search(Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(true)
Syntax: Mockito.when(className.methodName(Matchers)).thenReturn(value)
Let me breakdown the above code,
when
- takes parameter of what method to mock of the mocked class (Note: The class should be the class that we mocked with Mockito.mock(className)
otherwise you will see Exceptions)
Matchers
- Whenever the mocked method takes arguments, we can either specify the argument value or use Mockito.any()
as it is just a placeholder that matches any value.
thenReturn(value)
- this is used with when to specify what value to return. There are variants like .thenThrow
.thenAnswer
etc
Now let us verify, if the call actually goes to linearSearch with below code
Mockito.verify(linearSearch).search(Mockito.eq(new int[]{1,2,3}), Mockito.eq(1), Mockito.eq("LINEAR")));
Syntax: Mockito.verify(Mock_Class).Mock_method()
verify
checks if the mock method of the mocked class is called with specified parameter.Mockito.eq(value)
will check if the method is called with value as the parameter(for array it checks for each index match).
Final test code.
public class SearchWrapperTest {
Searchwrapper searchWrapper;
LinearSearch linearSearch;
@BeforeEach
public void setup() {
linearSearch = Mockito.mock(LinearSearch.class);
searchWrapper = new SearchWrapper(new BinarySearch(), );
}
@Test
public void testShouldLinearSearch(){
when(linearSearch.search(Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(true)
boolean expected = searchWrapper.search(new int[]{1,2,3} , 1, "LINEAR");
Assertions.assertTrue(expected);
Mockito.verify(linearSearch).search(Mockito.eq(new int[]{1,2,3}), Mockito.eq(1), Mockito.eq("LINEAR")));
}
}
Now try writing the same test for binarySearch.
Cheers.