A unit is the smallest testable part of an application. Mockito is a well known mock framework that allows us to create configure mock objects. With Mockito we can mock both interfaces and classes in the class under test. Mockito also helps us to reduce the number of boilerplate code while using mockito annotations.
Adding Mockito to the project
using gradle
testCompile "org.mockito:mockito−core:2.7.7"
using maven
<dependency>
<groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>2.7.7</version> <scope>test</scope>
</dependency>
Mockito annotations
@Mock – used for mock creation.
@Spy – creates a spy object.
@InjectMocks – instantiates the tested object and injects all the annotated field dependencies into it
@Captor – used to capture argument values for further assertions
testCompile "org.mockito:mockito−core:2.7.7"
using maven
<dependency>
<groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>2.7.7</version> <scope>test</scope>
</dependency>
Mockito annotations
@Mock – used for mock creation.
@Spy – creates a spy object.
@InjectMocks – instantiates the tested object and injects all the annotated field dependencies into it
@Captor – used to capture argument values for further assertions
Mockito example @Mock
Let’s say we have the following classes and we want to write a test for the CalculationService:
The usage of the @Mock and @InjectMock annotations is shown in the following sample code:
@Spy
Mockito spy is used to spying on a real object. The main difference between a spy and mock is that with spy the tested instance will behave as a normal instance. The following example will explain it:
Note that method add is called and the size of the spy list is 2.
Let’s say we have the following classes and we want to write a test for the CalculationService:
public class CalculationService {
private AddService addService;
public int calculate(int x, int y) {
return addService.add(x, y);
}
}
public class AddService {
public int add(int x, int y) {
return x+y;
}
}
The usage of the @Mock and @InjectMock annotations is shown in the following sample code:
@InjectMocks
private CalculationService calculationService;
@Mock
private AddService addService;
@Before
public void setUp() {
// initializes objects annotated with @Mock, @Spy, @Captor, or @InjectMocks
MockitoAnnotations.initMocks(this);
}
@Test
public void testCalculationService() {
// mock the result from method add in addService
doReturn(20).when(addService).add(10, 10);
// verify that the calculate method from calculationService will return the same value
assertEquals(20, calculationService.calculate(10, 10));
}
@Spy
Mockito spy is used to spying on a real object. The main difference between a spy and mock is that with spy the tested instance will behave as a normal instance. The following example will explain it:
@Test
public void testSpyInstance() {
List<String> spyList = spy(new ArrayList());
spyList.add("firstElement");
spyList.add("secondElement");
verify(spyList).add("firstElement");
verify(spyList).add("secondElement");
assertEquals(2, spyList.size());
}
Note that method add is called and the size of the spy list is 2.
@Captor
Mockito framework gives us plenty of useful annotations. One of the most recent that I’ve had a chance to use is @Captor. ArgumentCaptor is used to capture the inner data in a method that is either void or returns a different type of object.
Let’s say we have the following method snippet:
We want to capture the argument data so we can verify its inner data. So, to check that, we can use ArgumentCaptor from Mockito:
// Create a mock of the SearchData
SearchData data = mock(SearchData.class);
// Run the doSearch method with the mock
new AnyClass().doSearch(data);
// Capture the argument of the doSomething function
ArgumentCaptor<CustomData> captor = ArgumentCaptor.forClass(CustomData.class);
verify(data, times(1)).doSomething(captor.capture());
// Assert the argument
CustomData actualData = captor.getValue();
assertEquals("custom data", actualData.customData);
New features in Mockito 2.x
Since its inception, Mockito lacked mocking finals. One of the major features in the 2.X version is the support stubbing of the final method and final class. This feature has to be explicitly activated by creating the file MockMaker in this directory src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker containing a single line:
mock-maker-inline
Given the following example, without the file org.mockito.plugins.MockMaker and its content, we get the following error:
When the file is in the resources and the content is valid, we are all good.
The plan for the future is to have a programmatic way of using this feature.
Mockito framework gives us plenty of useful annotations. One of the most recent that I’ve had a chance to use is @Captor. ArgumentCaptor is used to capture the inner data in a method that is either void or returns a different type of object.
Let’s say we have the following method snippet:
public class AnyClass {
public void doSearch(SearchData searchData) {
CustomData data = new CustomData("custom data");
searchData.doSomething(data);
}
}
We want to capture the argument data so we can verify its inner data. So, to check that, we can use ArgumentCaptor from Mockito:
// Create a mock of the SearchData
SearchData data = mock(SearchData.class);
// Run the doSearch method with the mock
new AnyClass().doSearch(data);
// Capture the argument of the doSomething function
ArgumentCaptor<CustomData> captor = ArgumentCaptor.forClass(CustomData.class);
verify(data, times(1)).doSomething(captor.capture());
// Assert the argument
CustomData actualData = captor.getValue();
assertEquals("custom data", actualData.customData);
New features in Mockito 2.x
Since its inception, Mockito lacked mocking finals. One of the major features in the 2.X version is the support stubbing of the final method and final class. This feature has to be explicitly activated by creating the file MockMaker in this directory src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker containing a single line:
mock-maker-inline
public final class MyFinalClass {
public String hello() {
return "my final class says hello";
}
}
public class MyCallingClass {
final MyFinalClass myFinalClass = new MyFinalClass();
public String executeFinal() {
return myFinalClass.hello();
}
}
public class MyCallingClassTest {
@Test
public void testFinalClass() {
MyCallingClass myCallingClass = new MyCallingClass();
MyFinalClass myFinalClass = mock(MyFinalClass.java);
when(myFinalClass.hello()).thenReturn("testString");
assertEquals("testString", myCallingClass.executeFinal());
}
}
Given the following example, without the file org.mockito.plugins.MockMaker and its content, we get the following error:
When the file is in the resources and the content is valid, we are all good.
The plan for the future is to have a programmatic way of using this feature.
Conclusion
In this article, I gave a brief overview of some of the features in the Mockito test framework. Like any other tool, it must be used in a proper way to be useful. Now go and bring your unit tests to the next level.
In this article, I gave a brief overview of some of the features in the Mockito test framework. Like any other tool, it must be used in a proper way to be useful. Now go and bring your unit tests to the next level.
No comments:
Post a Comment
If you have any doubts or questions, please let us know.