During unit testing with junit and mockito, generally, we use @Mock and @InjectMocks annotations to create mocks and SUT (System Under Test) to be tested. In this tutorial, we will learn the difference between @Mock and @InjectMocks annotations in mockito.
1. What is a Mock?
It is important to understand the difference between a mock and an object. An object is an actual instance of a class and any method invoked using object reference will execute the method body defined in the class file.
A mock object is a proxy interface to hide an underlying dependency with cannot be tested in a test environment e.g. database, network locations etc. A method invoked using mocked reference does not execute the actual method body defined in the class file, rather the method behavior is configured using when(...).thenReturn(...)
methods.
- In a junit test, we create objects for the class which need to be tested and its methods to be invoked.
- We create mocks for the dependencies which will not be present in the test environment and objects are dependent on it to complete the method call.
2. Difference between @Mock and @InjectMocks
In mockito-based junit tests, @Mock annotation creates mocks and @InjectMocks creates actual objects and injects mocked dependencies into it.
- Use @InjectMocks to create class instances that need to be tested in the test class. We call it ‘code under test‘ or ‘system under test‘.
- Use @InjectMocks when actual method body needs to be executed for a given class.
- Use @InjectMocks when we need all or few internal dependencies initialized with mock objects to work method correctly.
- Use @Mock to create mocks that are needed to support the testing of SUT.
- We must define the
when(...).thenReturn(...)
methods for mock objects whose class methods will be invoked during actual test execution.
3. Demo
Let’s understand the difference between @Mock and @InjectMocks with an example. In this example, we have a class MainClass that has a method save().
MainClass
has a dependency on DatabaseDAO
and NetworkDAO
. When we call MainClass.save()
method, it internally calls save methods of both dependent objects.
3.1. System Under Test
public class MainClass {
DatabaseDAO database;
NetworkDAO network;
//Setters and getters
public boolean save(String fileName)
{
database.save(fileName);
System.out.println("Saved in database in Main class");
network.save(fileName);
System.out.println("Saved in network in Main class");
return true;
}
}
public class DatabaseDAO {
public void save(String fileName) {
System.out.println("Saved in database");
}
}
public class NetworkDAO {
public void save(String fileName) {
System.out.println("Saved in network location");
}
}
3.2. Unit Test
Let’s write the junit test for MainClass.
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
public class ApplicationTest
{
@InjectMocks
MainClass mainClass;
@Mock
DatabaseDAO dependentClassOne;
@Mock
NetworkDAO dependentClassTwo;
@Before
public void init() {
MockitoAnnotations.openMocks(this);
}
@Test
public void validateTest()
{
//record expectations with mock methods
when(dependentClassOne.save()).thenReturn(true);
when(dependentClassTwo.save()).thenReturn(true);
boolean saved = mainClass.save("temp.txt");
assertEquals(true, saved);
//verify recorded expectations
}
}
4. Conclusion
In this mockito tutorial, we learned the difference between @Mock and @InjectMocks annotations. We learned what happens when we apply these annotations to classes in junit tests.
Happy Learning !!
Leave a Reply