In the previous tutorial, we have seen Spring Boot Unit Testing CRUD REST API with JUnit and Mockito. In this tutorial, we will learn how to perform unit testing Spring boot service layer unit testing using JUnit 5 and the Mockito framework.
In order to test Service layer components, we have to mock the Repository layer components using the Mockito framework. We don't have to use a database for Unit testing.
Check out my Spring boot testing Udemy course: Testing Spring Boot Application with JUnit and Mockito (Includes Testcontainers)
Spring boot provides spring-boot-starter-test dependency for unit testing and integration testing of Spring boot application:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
The Spring Boot Starter Test dependency is a primary dependency for testing the Spring Boot Applications. It holds all the necessary elements required for the testing.
For the Unit testing service layer, we are going to use the following testing libraries:
- JUnit 5 Framework
- Mockito 4 (Latest)
- AssertJ Library
JUnit 5 Framework
Mockito 4 (Latest)
AssertJ Library
Tools and technologies used
- Java 11+
- Spring Boot
- Lombok
- JUnit 5 Framework
- Hamcrest
- AssertJ
- JsonPath
- Mockito
- IntelliJ IDEA
- Docker
- Maven
1. Create Spring Boot Application
- Spring Web
- Spring Data JPA
- Lombok
2. Maven Dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
3. Create JPA Entity
import lombok.*;
import javax.persistence.*;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
@Table(name = "employees")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "first_name", nullable = false)
private String firstName;
@Column(name = "last_name", nullable = false)
private String lastName;
@Column(nullable = false)
private String email;
}
4. Create Repository Layer
import net.javaguides.springboot.model.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
5. Create Service Layer
EmployeeService
import net.javaguides.springboot.model.Employee;
import java.util.List;
import java.util.Optional;
public interface EmployeeService {
Employee saveEmployee(Employee employee);
List<Employee> getAllEmployees();
Optional<Employee> getEmployeeById(long id);
Employee updateEmployee(Employee updatedEmployee);
void deleteEmployee(long id);
}
EmployeeServiceImpl
import net.javaguides.springboot.exception.ResourceNotFoundException;
import net.javaguides.springboot.model.Employee;
import net.javaguides.springboot.repository.EmployeeRepository;
import net.javaguides.springboot.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class EmployeeServiceImpl implements EmployeeService {
private EmployeeRepository employeeRepository;
public EmployeeServiceImpl(EmployeeRepository employeeRepository) {
this.employeeRepository = employeeRepository;
}
@Override
public Employee saveEmployee(Employee employee) {
Optional<Employee> savedEmployee = employeeRepository.findByEmail(employee.getEmail());
if(savedEmployee.isPresent()){
throw new ResourceNotFoundException("Employee already exist with given email:" + employee.getEmail());
}
return employeeRepository.save(employee);
}
@Override
public List<Employee> getAllEmployees() {
return employeeRepository.findAll();
}
@Override
public Optional<Employee> getEmployeeById(long id) {
return employeeRepository.findById(id);
}
@Override
public Employee updateEmployee(Employee updatedEmployee) {
return employeeRepository.save(updatedEmployee);
}
@Override
public void deleteEmployee(long id) {
employeeRepository.deleteById(id);
}
}
6. Unit Testing Service Layer using JUnit 5 and Mockito
Let us start writing unit tests for EmployeeService. We should be able to write unit tests for EmployeeService WITHOUT using any Spring features.We are going to create a mock of EmployeeRepository using @Mock and create an EmployeeServiceImpl instance using the mock EmployeeRepository instance.
import net.javaguides.springboot.exception.ResourceNotFoundException; import net.javaguides.springboot.model.Employee; import net.javaguides.springboot.repository.EmployeeRepository; import net.javaguides.springboot.service.impl.EmployeeServiceImpl; import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willDoNothing; import static org.mockito.Mockito.*; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.Collections; import java.util.List; import java.util.Optional; @ExtendWith(MockitoExtension.class) public class EmployeeServiceTests { @Mock private EmployeeRepository employeeRepository; @InjectMocks private EmployeeServiceImpl employeeService; private Employee employee; @BeforeEach public void setup(){ //employeeRepository = Mockito.mock(EmployeeRepository.class); //employeeService = new EmployeeServiceImpl(employeeRepository); employee = Employee.builder() .id(1L) .firstName("Ramesh") .lastName("Fadatare") .email("ramesh@gmail.com") .build(); } // JUnit test for saveEmployee method @DisplayName("JUnit test for saveEmployee method") @Test public void givenEmployeeObject_whenSaveEmployee_thenReturnEmployeeObject(){ // given - precondition or setup given(employeeRepository.findByEmail(employee.getEmail())) .willReturn(Optional.empty()); given(employeeRepository.save(employee)).willReturn(employee); System.out.println(employeeRepository); System.out.println(employeeService); // when - action or the behaviour that we are going test Employee savedEmployee = employeeService.saveEmployee(employee); System.out.println(savedEmployee); // then - verify the output assertThat(savedEmployee).isNotNull(); } // JUnit test for saveEmployee method @DisplayName("JUnit test for saveEmployee method which throws exception") @Test public void givenExistingEmail_whenSaveEmployee_thenThrowsException(){ // given - precondition or setup given(employeeRepository.findByEmail(employee.getEmail())) .willReturn(Optional.of(employee)); System.out.println(employeeRepository); System.out.println(employeeService); // when - action or the behaviour that we are going test org.junit.jupiter.api.Assertions.assertThrows(ResourceNotFoundException.class, () -> { employeeService.saveEmployee(employee); }); // then verify(employeeRepository, never()).save(any(Employee.class)); } // JUnit test for getAllEmployees method @DisplayName("JUnit test for getAllEmployees method") @Test public void givenEmployeesList_whenGetAllEmployees_thenReturnEmployeesList(){ // given - precondition or setup Employee employee1 = Employee.builder() .id(2L) .firstName("Tony") .lastName("Stark") .email("tony@gmail.com") .build(); given(employeeRepository.findAll()).willReturn(List.of(employee,employee1)); // when - action or the behaviour that we are going test List<Employee> employeeList = employeeService.getAllEmployees(); // then - verify the output assertThat(employeeList).isNotNull(); assertThat(employeeList.size()).isEqualTo(2); } // JUnit test for getAllEmployees method @DisplayName("JUnit test for getAllEmployees method (negative scenario)") @Test public void givenEmptyEmployeesList_whenGetAllEmployees_thenReturnEmptyEmployeesList(){ // given - precondition or setup Employee employee1 = Employee.builder() .id(2L) .firstName("Tony") .lastName("Stark") .email("tony@gmail.com") .build(); given(employeeRepository.findAll()).willReturn(Collections.emptyList()); // when - action or the behaviour that we are going test List<Employee> employeeList = employeeService.getAllEmployees(); // then - verify the output assertThat(employeeList).isEmpty(); assertThat(employeeList.size()).isEqualTo(0); } // JUnit test for getEmployeeById method @DisplayName("JUnit test for getEmployeeById method") @Test public void givenEmployeeId_whenGetEmployeeById_thenReturnEmployeeObject(){ // given given(employeeRepository.findById(1L)).willReturn(Optional.of(employee)); // when Employee savedEmployee = employeeService.getEmployeeById(employee.getId()).get(); // then assertThat(savedEmployee).isNotNull(); } // JUnit test for updateEmployee method @DisplayName("JUnit test for updateEmployee method") @Test public void givenEmployeeObject_whenUpdateEmployee_thenReturnUpdatedEmployee(){ // given - precondition or setup given(employeeRepository.save(employee)).willReturn(employee); employee.setEmail("ram@gmail.com"); employee.setFirstName("Ram"); // when - action or the behaviour that we are going test Employee updatedEmployee = employeeService.updateEmployee(employee); // then - verify the output assertThat(updatedEmployee.getEmail()).isEqualTo("ram@gmail.com"); assertThat(updatedEmployee.getFirstName()).isEqualTo("Ram"); } // JUnit test for deleteEmployee method @DisplayName("JUnit test for deleteEmployee method") @Test public void givenEmployeeId_whenDeleteEmployee_thenNothing(){ // given - precondition or setup long employeeId = 1L; willDoNothing().given(employeeRepository).deleteById(employeeId); // when - action or the behaviour that we are going test employeeService.deleteEmployee(employeeId); // then - verify the output verify(employeeRepository, times(1)).deleteById(employeeId); } }
JUnit test for saveEmployee method:
// JUnit test for saveEmployee method
@DisplayName("JUnit test for saveEmployee method")
@Test
public void givenEmployeeObject_whenSaveEmployee_thenReturnEmployeeObject(){
// given - precondition or setup
given(employeeRepository.findByEmail(employee.getEmail()))
.willReturn(Optional.empty());
given(employeeRepository.save(employee)).willReturn(employee);
System.out.println(employeeRepository);
System.out.println(employeeService);
// when - action or the behaviour that we are going test
Employee savedEmployee = employeeService.saveEmployee(employee);
System.out.println(savedEmployee);
// then - verify the output
assertThat(savedEmployee).isNotNull();
}
JUnit test for saveEmployee method which throws Exception:
// JUnit test for saveEmployee method
@DisplayName("JUnit test for saveEmployee method which throws exception")
@Test
public void givenExistingEmail_whenSaveEmployee_thenThrowsException(){
// given - precondition or setup
given(employeeRepository.findByEmail(employee.getEmail()))
.willReturn(Optional.of(employee));
System.out.println(employeeRepository);
System.out.println(employeeService);
// when - action or the behaviour that we are going test
org.junit.jupiter.api.Assertions.assertThrows(ResourceNotFoundException.class, () -> {
employeeService.saveEmployee(employee);
});
// then
verify(employeeRepository, never()).save(any(Employee.class));
}
JUnit test for getAllEmployees method:
@DisplayName("JUnit test for getAllEmployees method")
@Test
public void givenEmployeesList_whenGetAllEmployees_thenReturnEmployeesList(){
// given - precondition or setup
Employee employee1 = Employee.builder()
.id(2L)
.firstName("Tony")
.lastName("Stark")
.email("tony@gmail.com")
.build();
given(employeeRepository.findAll()).willReturn(List.of(employee,employee1));
// when - action or the behaviour that we are going test
List<Employee> employeeList = employeeService.getAllEmployees();
// then - verify the output
assertThat(employeeList).isNotNull();
assertThat(employeeList.size()).isEqualTo(2);
}
JUnit test for getEmployeeById method
// JUnit test for getEmployeeById method
@DisplayName("JUnit test for getEmployeeById method")
@Test
public void givenEmployeeId_whenGetEmployeeById_thenReturnEmployeeObject(){
// given
given(employeeRepository.findById(1L)).willReturn(Optional.of(employee));
// when
Employee savedEmployee = employeeService.getEmployeeById(employee.getId()).get();
// then
assertThat(savedEmployee).isNotNull();
}
JUnit test for updateEmployee method:
// JUnit test for updateEmployee method
@DisplayName("JUnit test for updateEmployee method")
@Test
public void givenEmployeeObject_whenUpdateEmployee_thenReturnUpdatedEmployee(){
// given - precondition or setup
given(employeeRepository.save(employee)).willReturn(employee);
employee.setEmail("ram@gmail.com");
employee.setFirstName("Ram");
// when - action or the behaviour that we are going test
Employee updatedEmployee = employeeService.updateEmployee(employee);
// then - verify the output
assertThat(updatedEmployee.getEmail()).isEqualTo("ram@gmail.com");
assertThat(updatedEmployee.getFirstName()).isEqualTo("Ram");
}
JUnit test for deleteEmployee method
// JUnit test for deleteEmployee method
@DisplayName("JUnit test for deleteEmployee method")
@Test
public void givenEmployeeId_whenDeleteEmployee_thenNothing(){
// given - precondition or setup
long employeeId = 1L;
willDoNothing().given(employeeRepository).deleteById(employeeId);
// when - action or the behaviour that we are going test
employeeService.deleteEmployee(employeeId);
// then - verify the output
verify(employeeRepository, times(1)).deleteById(employeeId);
}
7. Demo - Run JUnit Tests
8. Conclusion
In this tutorial, we have discussed how to perform Spring Boot unit testing CRUD REST APIs using JUnit and Mockito frameworks.
If you want to learn more about Spring boot testing then highly suggest my Udemy course: Testing Spring Boot Application with JUnit and Mockito (Includes Testcontainers)
9. Spring Boot Testing Tutorials and Guides
- Spring Boot Unit Testing CRUD REST API with JUnit and Mockito
- Spring Boot Testing - Data Access Layer Integration Testing using Testcontainers
- Spring Boot Testing - REST API Integration Testing using Testcontainers
- Spring Data JPA Repository Testing using Spring Boot @DataJpaTest
- CRUD JUnit Tests for Spring Data JPA - Testing Repository Layer
- Spring Boot Integration Testing MySQL CRUD REST API Tutorial
Comments
Post a Comment
Leave Comment