Menu Close

使用Spring Boot @DataJpaTest测试Spring Data JPA Repository

我们在测试应用程序的持久层组件时,是不需要加载许多组件,如 controllers, security 等。因此,Spring Boot 提供了@DataJpaTest注解来测试我们的Spring Boot应用程序唯一的 repository/persistence layer components (@DataJpaTest注解不会将其他SpringBean(@Components, @Controller, @Service, 和 annotated beans) 加载到ApplicationContext中)。

1. @DataJpaTest 注解概述

  1. Spring Boot提供了@DataJpaTest注解来测试持久层组件,这些组件将出于测试目的自动配置内存中的嵌入式数据库。

  2. @DataJpaTest注解不会将其他SpringBean(@Components、@Controller、@Service和带注解的Bean)加载到ApplicationContext中。默认情况下,它扫描@Entity类并配置带有@Repository注解的Spring data JPA存储库。

  3. 默认情况下,使用@DataJpaTest注解的测试是事务性的,并在每次测试结束时回滚。

  4. 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项目。

file

在创建Spring Boot时使用以下详细信息:

  • Project Name: spring-boot-testing

  • Project Type: Gradle-kotlin

  • Choose dependencies: Spring Data JPA, H2 database, Lombok

  • Package name: xin.qishuo.springboottesting

file

2.3. 配置Gradle

通过 文件 -> 设置 -> 构建、执行、部署 -> 构建工具 -> Gradle 来设置 Gradle 的一些属性。

file

同时修改 ./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操作的方法,以及名为SimpleJpaRepositoryJpaRepository的默认实现。

先创建一个名为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());
    }
}

2.7. 运行 JUnit Tests

file

附录

附录A. 相关联的文章

附录B. 其他参考