Spring’s controller/service/repository layering means the repository talks to the DB, and the service talks to the repository. To test the service layer in isolation, we need to fake its repository dependency — that’s mocking, and Mockito makes it possible.
Mocking dependencies with Mockito
Mockito.mock()— creates a mock object of a given class/interface; the simplest way to mock.@Mock— also mocks an object, but is especially useful when the mock is used in multiple places, since it avoids repeatedmock()calls and makes the code more readable (and gives the mock a name that shows up in error messages).@InjectMocks— injects mocked objects into another object under test. It creates an instance of the class and injects any fields marked@Mockinto it.
@ExtendWith(MockitoExtension.class)public class EmployeeServiceTest { @Mock private EmployeeRepository employeeRepository;
@InjectMocks private EmployeeServiceImpl employeeService;}This mocks EmployeeServiceTest’s dependency and injects employeeRepository into
employeeService. Mockito also has a BDDMockito class for BDD-style given(...).willReturn(...)
assertions. A full service-layer test class:
@ExtendWith(MockitoExtension.class)class EmployeeServiceImplTest {
@Mock private EmployeeRepository employeeRepository;
@InjectMocks private EmployeeServiceImpl employeeService;
private Employee employee;
@BeforeEach public void setup() { employee = Employee.builder() .id(1L) .firstName("Ernest") .lastName("Sarfo") .email("sarfo@gmail.com") .build(); }
@DisplayName("Unit test for saveEmployee method") @Test void givenEmployeeObject_whenSaveEmployee_thenReturnEmployeeObject() { given(employeeRepository.findByEmail(employee.getEmail())) .willReturn(Optional.empty()); given(employeeRepository.save(employee)).willReturn(employee);
Employee savedEmployee = employeeService.saveEmployee(employee);
assertThat(savedEmployee).isNotNull(); }
@DisplayName("Unit test for saveEmployee method which throws exception") @Test void givenExistingEmail_whenSaveEmployee_thenThrowsException() { given(employeeRepository.findByEmail(employee.getEmail())) .willReturn(Optional.of(employee));
assertThrows(ResourceNotFoundException.class, () -> employeeService.saveEmployee(employee));
verify(employeeRepository, never()).save(any(Employee.class)); }
@DisplayName("Unit test for getAllEmployeesMethod") @Test void givenEmployeeList_whenGetAllEmployees_thenReturnEmployees() { Employee employee1 = Employee.builder() .id(2L) .firstName("Tony") .lastName("Stark") .email("stark@gmail.com") .build(); given(employeeRepository.findAll()).willReturn(List.of(employee, employee1));
List<Employee> employees = employeeService.getAllEmployees();
assertThat(employees).isNotNull(); assertThat(employees).hasSize(2); }
@DisplayName("Unit test for getAllEmployeesMethod (negative scenario)") @Test void givenEmptyEmployeeList_whenGetAllEmployees_thenReturnEmptyEmployeeList() { given(employeeRepository.findAll()).willReturn(Collections.emptyList());
List<Employee> employees = employeeService.getAllEmployees();
assertThat(employees).isEmpty(); }
@DisplayName("Unit test for get employee by id method") @Test void givenEmployeeId_whenFindById_thenReturnEmployeeObject() { given(employeeRepository.findById(1L)).willReturn(Optional.of(employee));
Optional<Employee> savedEmployee = employeeService.getEmployeeById(1L);
assertThat(savedEmployee).isNotNull(); assertThat(savedEmployee.get().getId()).isEqualTo(1L); }
@DisplayName("Unit test for update employee method") @Test void givenEmployeeObject_whenUpdateEmployee_thenReturnUpdatedEmployee() { given(employeeRepository.save(employee)).willReturn(employee); employee.setEmail("travis@gmailcom"); employee.setFirstName("travis"); employee.setLastName("scott");
Employee updatedEmployee = employeeService.updateEmployee(employee);
assertThat(updatedEmployee).isNotNull(); assertThat(updatedEmployee.getFirstName()).isEqualTo("travis"); }
@DisplayName("Unit test for delete employee method") @Test void givenEmployee_whenDeleteEmployee_thenReturnNothing() { long employeeId = 1L; willDoNothing().given(employeeRepository).deleteById(employeeId);
employeeService.deleteEmployee(employeeId);
verify(employeeRepository, times(1)).deleteById(employeeId); }}