Learn to write unit tests for the service layer of Spring boot applications using JUnit 5 and Mockito testing frameworks.
1. Maven Dependencies
The spring-boot-starter-test dependency transitively imports JUnit 5 and Mockito. So we only need to include this dependency.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2. Initializing Mocks
In this example, we are unit testing primarily two classes EmployeeManager
and EmployeeDao
. As the name implies, the manager class represents the service layer, and dao class interacts with the database.
EmployeeManager
class has a dependency on EmployeeDao
to get the data from the database that is finally returned to controller classes. To test the methods in EmployeeManager
, we can create a JUnit test class TestEmployeeManager in below given two ways:
2.1. @Mock vs @InjectMocks
- The @Mock annotation creates a mock implementation for the class it is annotated with.
- @InjectMocks also creates the mock implementation of annotated type and injects the dependent mocks into it.
In the above example, we have annotated EmployeeManager
class with @InjectMocks
, so mockito will create the mock object for EmployeeManager
class and inject the mock dependency of EmployeeDao
into it.
2.2. Initialization with MockitoExtension
To process Mockito annotations with JUnit, we need to use MockitoExtention that automatically initializes all the objects annotated with @Mock
and @InjectMocks
annotations.
@ExtendWith(MockitoExtension.class)
public class TestEmployeeManager {
@InjectMocks
EmployeeManager manager;
@Mock
EmployeeDao dao;
//tests
}
2.3. Initialization with MockitoAnnotations.openMocks()
If we are not using the MockitoJUnitRunner class approach, then we can use the static method MockitoAnnotations.initMocks()
. Upon initialization of junit tests, this method also initializes the mock objects.
public class TestEmployeeManager {
@InjectMocks
EmployeeManager manager;
@Mock
EmployeeDao dao;
@Before
public void init() {
MockitoAnnotations.openMocks(this);
}
//tests
}
3. JUnit Tests
Let’s see a few examples of writing the junit tests to unit test the service layer and DAO layer methods using mock objects created with mockito.
A few example methods could be for getAllEmployees() returning a list of EmployeeVO
objects, getEmployeeById(int id)
for returning an employee by given id; and createEmployee()
for adding an employee object and return void
.
3.1. Service Class Tests
The following class contains the tests for service class methods. It uses Mockito.when() methods to create test stubs.
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import com.howtodoinjava.demo.dao.EmployeeDao;
import com.howtodoinjava.demo.model.EmployeeVO;
import com.howtodoinjava.demo.service.EmployeeManager;
public class TestEmployeeManager {
@InjectMocks
EmployeeManager manager;
@Mock
EmployeeDao dao;
@Before
public void init() {
MockitoAnnotations.openMocks(this);
}
@Test
public void getAllEmployeesTest()
{
List<EmployeeVO> list = new ArrayList<EmployeeVO>();
EmployeeVO empOne = new EmployeeVO(1, "John", "John", "howtodoinjava@gmail.com");
EmployeeVO empTwo = new EmployeeVO(2, "Alex", "kolenchiski", "alexk@yahoo.com");
EmployeeVO empThree = new EmployeeVO(3, "Steve", "Waugh", "swaugh@gmail.com");
list.add(empOne);
list.add(empTwo);
list.add(empThree);
when(dao.getEmployeeList()).thenReturn(list);
//test
List<EmployeeVO> empList = manager.getEmployeeList();
assertEquals(3, empList.size());
verify(dao, times(1)).getEmployeeList();
}
@Test
public void getEmployeeByIdTest()
{
when(dao.getEmployeeById(1)).thenReturn(new EmployeeVO(1,"Lokesh","Gupta","user@email.com"));
EmployeeVO emp = manager.getEmployeeById(1);
assertEquals("Lokesh", emp.getFirstName());
assertEquals("Gupta", emp.getLastName());
assertEquals("user@email.com", emp.getEmail());
}
@Test
public void createEmployeeTest()
{
EmployeeVO emp = new EmployeeVO(1,"Lokesh","Gupta","user@email.com");
manager.addEmployee(emp);
verify(dao, times(1)).addEmployee(emp);
}
}
Please note that Mockito throws an UnsupportedStubbingException when an initialized mock is not called during test execution. If there are such optional mocked behavior the use Mockito.lenient() as follows:
Mockito.lenient().when(dao.isEmployeeDeleted()).thenReturn(Boolean.False);
3.2. Service Class
For reference, the service class is as follows:
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.howtodoinjava.demo.dao.EmployeeDao;
import com.howtodoinjava.demo.model.EmployeeVO;
@Service
public class EmployeeManager
{
@Autowired
EmployeeDao dao;
public List<EmployeeVO> getEmployeeList() {
return dao.getEmployeeList();
}
public EmployeeVO getEmployeeById(int id) {
return dao.getEmployeeById(id);
}
public void addEmployee(EmployeeVO employee) {
dao.addEmployee(employee);
}
}
3.3. Dao Layer Class
Also, for reference, the DAO class is as follows:
package com.howtodoinjava.demo.dao;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Repository;
import com.howtodoinjava.demo.model.EmployeeVO;
@Repository
public class EmployeeDao {
private Map<Integer, EmployeeVO> DB = new HashMap<>();
public List<EmployeeVO> getEmployeeList()
{
List<EmployeeVO> list = new ArrayList<>();
if(list.isEmpty()) {
list.addAll(DB.values());
}
return list;
}
public EmployeeVO getEmployeeById(int id) {
return DB.get(id);
}
public void addEmployee(EmployeeVO employee) {
employee.setEmployeeId(DB.keySet().size() + 1);
DB.put(employee.getEmployeeId(), employee);
}
public void updateEmployee(EmployeeVO employee) {
DB.put(employee.getEmployeeId(), employee);
}
public void deleteEmployee(int id) {
DB.remove(id);
}
}
4. Conclusion
This mockito tutorial taught us to unit test the service layer in spring boot applications using JUnit and Mockito. We learned to setup the test class and to write JUnit tests.We also learned the difference between @Mock and @InjectMocks annotations.
Happy Learning !!