我们在测试应用程序的持久层组件时,是不需要加载许多组件,如 controllers, security 等。因此,Spring Boot 提供了@DataJpaTest
注解来测试我们的Spring Boot应用程序唯一的 repository/persistence layer components (@DataJpaTest
注解不会将其他SpringBean(@Components, @Controller, @Service, 和 annotated beans
) 加载到ApplicationContext中)。
1. @DataJpaTest 注解概述
-
Spring Boot提供了
@DataJpaTest
注解来测试持久层组件,这些组件将出于测试目的自动配置内存中的嵌入式数据库。 -
@DataJpaTest
注解不会将其他SpringBean(@Components、@Controller、@Service和带注解的Bean)
加载到ApplicationContext
中。默认情况下,它扫描@Entity
类并配置带有@Repository
注解的Spring data JPA
存储库。 -
默认情况下,使用
@DataJpaTest
注解的测试是事务性的,并在每次测试结束时回滚。 -
Spring Boot提供的
@DataJpaTest
注解不仅用于测试Spring Data JPA Repository
,还支持测试与JPA相关的组件。
2. 在Spring Boot项目中使用@DataJpaTest
2.1. 使用的工具和技术
- Spring Boot - 2.7.6
- JDK - 1.8 or later
- Spring Framework - 5.3.24
- Hibernate - 5.6.14
- H2
- Gradle - 7.5+
- IDE - IntelliJ IDEA
2.2. 创建并导入项目
Spring Boot提供了一个名为https://start.spring.io
的Web工具来快速引导应用程序。只需转到https://start.spring.io
并生成一个新的Spring Boot项目。
在创建Spring Boot时使用以下详细信息:
-
Project Name: spring-boot-testing
-
Project Type: Gradle-kotlin
-
Choose dependencies: Spring Data JPA, H2 database, Lombok
-
Package name: xin.qishuo.springboottesting
2.3. 配置Gradle
通过 文件 -> 设置 -> 构建、执行、部署 -> 构建工具 -> Gradle
来设置 Gradle 的一些属性。
同时修改 ./gradle/wrapper/gradle-wrapper.properties
文件,gradle 使用本地下载的 7.5版本,同时使用代理IP(不需要代理可删除相关配置)
gradle-wrapper.properties文件样例
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=file:///data/gradle-7.5/gradle-7.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
systemProp.http.proxyHost=XXX.XXX.XXX.XXX
systemProp.http.proxyPort=3128
systemProp.https.proxyHost=XXX.XXX.XXX.XXX
systemProp.https.proxyPort=3128
2.3. Maven依赖项
下面是完整的build.gradle.kts
,供您参考:
build.gradle.kts文件样例
plugins {
java
id("org.springframework.boot") version "2.7.6"
id("io.spring.dependency-management") version "1.0.15.RELEASE"
}
group = "xin.qishuo"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_1_8
configurations {
compileOnly {
extendsFrom(configurations.annotationProcessor.get())
}
}
repositories {
maven ("https://maven.aliyun.com/repository/jcenter")
maven ( "https://maven.aliyun.com/repository/google")
maven ( "https://maven.aliyun.com/repository/central")
maven ( "https://maven.aliyun.com/repository/gradle-plugin")
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
compileOnly("org.projectlombok:lombok")
runtimeOnly("com.h2database:h2")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.withType<Test> {
useJUnitPlatform()
}
2.4. 创建JPA Entity-Employee.Java
先创建一个名为entity
的包,然后在其下创建一个Employee
类,其中包含以下内容:
实体类Employee的样板代码
package xin.qishuo.springboottesting.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
@Table(name = "employee")
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;
}
请注意,我们使用Lombok注解来减少样板代码。
介绍一下 @Builder 注解
lombok
注解在java
进行编译时进行代码的构建,对于java
对象的创建工作它可以更优雅,不需要写多余的重复的代码,这对于JAVA开发人员是很重要的,在出现lombok
之后,对象的创建工作更提供Builder
方法,它提供在设计数据实体时,对外保持private setter
,而对属性的赋值采用Builder
的方式,这种方式最优雅,也更符合封装的原则,不对外公开属性的写操作!@Builder
声明实体,表示可以进行Builder
方式初始化,@Value
注解,表示只公开getter
,对所有属性的setter
都封闭,即private
修饰,所以它不能和@Builder
一起用。
2.5. 创建 Spring Data JPA Repository
接下来我们创建一个repository
来访问数据库中Employee表中的数据。
JpaRepository
接口定义了实体上所有CRUD
操作的方法,以及名为SimpleJpaRepository
的JpaRepository
的默认实现。
先创建一个名为repository
的包,并在其下创建EmployeeRepository
接口,使其继承JpaRepository
。具体内容如下:
EmployeeRepository接口的样板代码
import org.springframework.data.jpa.repository.JpaRepository;
import xin.qishuo.springboottesting.entity.Employee;
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
2.6. 使用 JUnit 测试 Spring Data JPA Repository
在test目录中创建一下EmployeeRepositoryTests
的测试类。
EmployeeRepositoryTests类的样板代码
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import xin.qishuo.springboottesting.entity.Employee;
import xin.qishuo.springboottesting.repository.EmployeeRepository;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
public class EmployeeRepositoryTests {
@Autowired
private EmployeeRepository employeeRepository;
private Employee employee;
@BeforeEach
public void setup() {
employee = Employee.builder()
.firstName("Zhang")
.lastName("San")
.email("zhangs@gmail.com")
.build();
}
@DisplayName("JUnit test for save employee operation")
@Test
public void givenEmployeeObject_whenSave_thenReturnSavedEmployee() {
// given - precondition or setup
Employee employee1 = Employee.builder()
.firstName("Li")
.lastName("Si")
.email("lis@gmail.com")
.build();
// when - action or behaviour that we are going test
Employee savedEmployee = employeeRepository.save(employee1);
// then - verify the output
assertThat(savedEmployee).isNotNull();
assertThat(savedEmployee.getId()).isGreaterThan(0);
}
@DisplayName("JUnit test for get all employees operation")
@Test
public void givenEmployeeList_whenFindAll_thenEmployeeList() {
// given - precondition or setup
Employee employee1 = Employee.builder()
.firstName("Wang")
.lastName("Wu")
.email("wangw@gmail.com")
.build();
employeeRepository.save(employee);
employeeRepository.save(employee1);
// when - action or the behaviour that we are going test
List<Employee> employeeList = employeeRepository.findAll();
// then - verify the output
assertThat(employeeList).isNotNull();
assertThat(employeeList.size()).isEqualTo(2);
System.out.println(employeeList.toString());
}
}