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.
@ExtendWith(MockitoExtension.class)
public class ItemServiceTest {
@Mock
private ItemRepository itemRepository;
@InjectMocks
private ItemService itemService; // ItemService uses ItemRepository
@Test
public void testCreateItem() {
// ...
}
}
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 that 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 that 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 the actual method body needs to be executed for a given class.
- Use @InjectMocks when we need all or a few internal dependencies initialized with mock objects to work the 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 boolean save(String fileName) {
System.out.println("Saved in database");
return true;
}
}
public class NetworkDAO {
public void save(String fileName) {
System.out.println("Saved in network location");
return true;
}
}
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 !!
hi Lokesh, what if we want to mock a method defined in class annoted with @InjeckMock class
Use Mockito.spy(). The spied class uses the real methods for all calls except for those that you’ve specifically mocked.
This example doesn’t seem correct. These 2 save() methods require a String parameter and don’t return a boolean:
when(dependentClassOne.save()).thenReturn(true);
when(dependentClassTwo.save()).thenReturn(true);
“public boolean save(String fileName)” is the method declaration. What are you referring to?
public boolean save(String fileName) is a method in Main.class.
You are trying to mock public void save(String fileName) in DatabaseDAO and NetworkDAO.
Oh. Thanks for noticing and reporting. Its updated now.
It is Injectmock not initmock.
Not when you use the MockitoAnnotations.initMocks method.