In this tutorial, we will learn how to test repository or DAO layer using Spring boot provided @DataJpaTest annotation.
We will write a JUnit 5 test case for CRUD operations - Create, Read, Update and Delete.
The Spring boot provides @DataJpaTest annotation. This annotation will disable full auto-configuration and instead apply only configuration relevant to JPA tests. By default, it will use an embedded, in-memory H2 database instead of the one declared in the configuration file, for faster test running time as compared to disk file database.
The @DataJpaTest annotation doesn’t load other Spring beans (@Component, @Controller, @Service, and annotated beans) into ApplicationContext.
We will create Spring Boot project from scratch using Spring data JPA ( Hibernate) and H2 database.
Spring Boot provides a web tool called https://start.spring.io to bootstrap an application quickly. Just go to https://start.spring.io and generate a new spring boot project.
Use the below details in the Spring boot creation:
Project Name: spring-data-data-testing
Project Type: Maven
Choose dependencies: Spring Data JPA, H2 database, Lombok, Spring Boot Dev Tools
Package name: net.javaguides.springboot
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </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>
Let's create an entity package inside a base package "net.javaguides.springboot". Within the entity package, create a Employee class with the following content:
package net.javaguides.springboot; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import jakarta.persistence.*; @Data @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(name = "email", nullable = false) private String email; }
The annotations used are:
@Data: This annotation is from the Lombok library and is used to automatically generate getters and setters for all fields in the class, as well as implementations of toString(), equals(), and hashCode(). This reduces the amount of boilerplate code that needs to be written.
@AllArgsConstructor: This annotation is also from Lombok and generates a constructor that takes all of the fields in the class as arguments.
@NoArgsConstructor: This annotation is also from Lombok and generates a no-argument constructor.
@Builder: This annotation is also from Lombok and generates a builder pattern for the class, which provides a convenient way to create instances of the class with default or optional values for some fields.
@Entity: This annotation is from the Java Persistence API (JPA) and marks the class as an entity that can be persisted to a database.
@Table(name = "employees"): This annotation specifies the name of the table that corresponds to this entity in the database.
The next thing we’re gonna do is create a repository to access a User’s data from the database.
The JpaRepository interface defines methods for all the CRUD operations on the entity, and a default implementation of the JpaRepository called SimpleJpaRepository.
Let's create a repository package inside a base package "net.javaguides.springdatarest". Within the repository package, create a EmployeeRepository interface with the following content:
package net.javaguides.springboot; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; public interface EmployeeRepository extends JpaRepository<Employee, Long> { Optional<Employee> findByEmail(String email); }
Spring Boot provides the @DataJpaTest annotation to test the persistence layer components that will autoconfigure in-memory embedded databases and scan for @Entity classes and Spring Data JPA repositories. The @DataJpaTest annotation doesn’t load other Spring beans (@Components, @Controller, @Service, and annotated beans) into ApplicationContext.
We need to specify the execution order because JUnit doesn’t run test methods in the order they appear in the code. So we need to use the @TestMethodOrder and @Order annotations to execute test cases in ascending order.
Head over to test package. Let's create a repository package inside a base package "test.net.javaguides.springboot". Within the repository package, create a EmployeeRepositoryTest class with the following content:
package net.javaguides.springboot; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.annotation.Rollback; import javax.sql.DataSource; import java.util.List; import java.util.Optional; @DataJpaTest @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class EmployeeRepositoryTests { @Autowired private EmployeeRepository employeeRepository; // JUnit test for saveEmployee @Test @Order(1) @Rollback(value = false) public void saveEmployeeTest(){ Employee employee = Employee.builder() .firstName("Ramesh") .lastName("Fadatare") .email("ramesh@gmail.com") .build(); employeeRepository.save(employee); Assertions.assertThat(employee.getId()).isGreaterThan(0); } @Test @Order(2) public void getEmployeeTest(){ Employee employee = employeeRepository.findById(1L).get(); Assertions.assertThat(employee.getId()).isEqualTo(1L); } @Test @Order(3) public void getListOfEmployeesTest(){ List<Employee> employees = employeeRepository.findAll(); Assertions.assertThat(employees.size()).isGreaterThan(0); } @Test @Order(4) @Rollback(value = false) public void updateEmployeeTest(){ Employee employee = employeeRepository.findById(1L).get(); employee.setEmail("ram@gmail.com"); Employee employeeUpdated = employeeRepository.save(employee); Assertions.assertThat(employeeUpdated.getEmail()).isEqualTo("ram@gmail.com"); } @Test @Order(5) @Rollback(value = false) public void deleteEmployeeTest(){ Employee employee = employeeRepository.findById(1L).get(); employeeRepository.delete(employee); //employeeRepository.deleteById(1L); Employee employee1 = null; Optional<Employee> optionalEmployee = employeeRepository.findByEmail("ram@gmail.com"); if(optionalEmployee.isPresent()){ employee1 = optionalEmployee.get(); } Assertions.assertThat(employee1).isNull(); } }
Let's understand above JUnit test cases one by one.
// JUnit test for saveEmployee @Test @Order(1) @Rollback(value = false) public void saveEmployeeTest(){ Employee employee = Employee.builder() .firstName("Ramesh") .lastName("Fadatare") .email("ramesh@gmail.com") .build(); employeeRepository.save(employee); Assertions.assertThat(employee.getId()).isGreaterThan(0); }
Note that we have used @Rollback(false) to disable roll back to the data will be committed to the database and available for the next test methods which will run separately. We have also used assertThat() method from AssertJ library for more readability than using JUnit’s assertion methods.
We write a test to retrieve employee by it's Id and test the same with id 1:
@Test @Order(2) public void getEmployeeTest(){ Employee employee = employeeRepository.findById(1L).get(); Assertions.assertThat(employee.getId()).isEqualTo(1L); }
We have also used assertThat() method from AssertJ library for more readability than using JUnit’s assertion methods.
We write a test method to retrieve list of employees from database and test it's size:
@Test @Order(3) public void getListOfEmployeesTest(){ List<Employee> employees = employeeRepository.findAll(); Assertions.assertThat(employees.size()).isGreaterThan(0); }
We have also used assertThat() method from AssertJ library for more readability than using JUnit’s assertion methods.
We write a JUnit test method to test update employee operation:
@Test @Order(4) @Rollback(value = false) public void updateEmployeeTest(){ Employee employee = employeeRepository.findById(1L).get(); employee.setEmail("ram@gmail.com"); Employee employeeUpdated = employeeRepository.save(employee); Assertions.assertThat(employeeUpdated.getEmail()).isEqualTo("ram@gmail.com"); }
Note that we have used @Rollback(false) to disable roll back to the data will be committed to the database and available for the next test methods which will run separately.
We have also used assertThat() method from AssertJ library for more readability than using JUnit’s assertion methods.
We write a JUnit test method to test delete employee operation:
@Test @Order(5) @Rollback(value = false) public void deleteEmployeeTest(){ Employee employee = employeeRepository.findById(1L).get(); employeeRepository.delete(employee); //employeeRepository.deleteById(1L); Employee employee1 = null; Optional<Employee> optionalEmployee = employeeRepository.findByEmail("ram@gmail.com"); if(optionalEmployee.isPresent()){ employee1 = optionalEmployee.get(); } Assertions.assertThat(employee1).isNull(); }
Here the output of the CRUD JUnit tests: