@DataJpaTest的Spring Data Repository单元测试示例

12,197次阅读
没有评论

共计 10267 个字符,预计需要花费 26 分钟才能阅读完成。

如今,在软件开发中,单元测试非常重要,Spring 框架也提供了 @DataJpaTest 注解,使得编写 JPA Repository 的测试更加简单。在本教程中,我们将学习如何在 Spring Boot 项目中应用 @DataJpaTest,并结合 TestEntityManager 和 JUnit 5 进行运行。

Spring Boot @DataJpaTest 示例概述

我们有一个 Tutorial 模型,包含一些字段:id、title、description、published。

有一个用于与数据库交互的存储库,名为 TutorialRepository 接口,它扩展了 JpaRepository:

public interface TutorialRepository extends JpaRepository {List findByPublished(boolean published);
  List findByTitleContaining(String title);
}

JpaRepository 还支持以下方法:save()、findOne()、findById()、findAll()、count()、delete()、deleteById()。

那么如何为它们编写单元测试呢?

=> 我们将使用 @DataJpaTest 和 TestEntityManager 来进行测试。

@RunWith(SpringRunner.class)
@DataJpaTest
public class JPAUnitTest {

  @Autowired
  private TestEntityManager entityManager;

  @Autowired
  TutorialRepository repository;

  @Test
  public void should_find_no_tutorials_if_repository_is_empty() {}

  @Test
  public void should_store_a_tutorial() {}

  @Test
  public void should_find_all_tutorials() {}

  @Test
  public void should_find_tutorial_by_id() {}

  @Test
  public void should_find_published_tutorials() {}

  @Test
  public void should_find_tutorials_by_title_containing_string() {}

  @Test
  public void should_update_tutorial_by_id() {}

  @Test
  public void should_delete_tutorial_by_id() {}

  @Test
  public void should_delete_all_tutorials() {}
}

对于测试,我们将使用 H2 内存数据库。它消除了配置和启动实际数据库的需要。

@DataJpaTest 注解用于测试 JPA Repository

@DataJpaTest 是 Spring 支持的专注于 JPA 组件的 JPA 测试注解。

它将禁用完整的自动配置,然后仅应用与 JPA 测试相关的启用配置。可以在此处找到启用的自动配置设置列表。

默认情况下,使用 @DataJpaTest 注解的测试是事务性的,并且在每个测试结束时进行回滚。如果不希望这样,可以使用 @Transactional 注解禁用测试或整个类的事务管理:

@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class YourNonTransactionalTests {}

内存嵌入式数据库(如本示例中的 H2 数据库)通常对测试效果良好,它运行速度快,不需要任何安装。然而,我们可以通过 @AutoConfigureTestDatabase 注解来配置真实数据库:

@DataJpaTest
@AutoConfigureTestDatabase(replace=Replace.NONE)
class YourRepositoryTests {}

如果使用 JUnit 4,则需要在测试中添加 @RunWith(SpringRunner.class):

@RunWith(SpringRunner.class)
@DataJpaTest
class YourRepositoryTests {}

TestEntityManager

EntityManager 的目的是与持久化上下文进行交互。Spring Data JPA 通过 Repository 接口将您从 EntityManager 中抽象出来。而 TestEntityManager 允许我们在测试中使用 EntityManager。

我们可以在 Data JPA 测试中注入一个 TestEntityManager bean。如果您想在 @DataJpaTest 实例之外使用 TestEntityManager,只需添加 @AutoConfigureTestEntityManager 注解。

以下示例展示了带有 TestEntityManager 的 @DataJpaTest 注解:

@DataJpaTest
class YourRepositoryTests {

  @Autowired
  private TestEntityManager entityManager;

  @Test
  void testExample() throws Exception {this.entityManager.persist(new Tutorial("Tut#1", "Desc#1", true));
    ...
  }
}

在这个示例中,TestEntityManager 被注入并可以在测试方法中使用,以进行与实际数据库的交互和操作。

项目结构

让我们来看一下我们的 Spring Boot 项目:

Spring Boot 项目结构

  • Tutorial 数据模型类对应于实体和名为 ”tutorials” 的表。

  • TutorialRepository 是一个接口,它扩展了 JpaRepository,提供了 CRUD 方法和自定义查找方法。它将在 JPAUnitTest 中被自动装配(Autowired)使用。

  • JPAUnitTest 是主要的测试类,用于测试 JPA,并使用 @DataJpaTest 注解进行标记。

  • pom.xml 包含了 Spring Boot、JPA 和 H2 数据库的依赖项。

搭建 Spring Boot @DataJpaTest 项目

使用 Spring Web 工具或你的开发工具(如 Spring Tool Suite、Eclipse、Intellij)创建一个 Spring Boot 项目。

然后打开 pom.xml 文件并添加以下依赖项:


    org.springframework.boot
    spring-boot-starter-data-jpa



    org.springframework.boot
    spring-boot-starter-test
    test



    com.h2database
    h2
    test

定义数据模型

在 model 包中,我们定义 Tutorial 类。

我们的数据模型(实体)包含 4 个字段:id、title、description、published。

model/Tutorial.java

package com.bezkoder.spring.data.jpa.test.model;
import javax.persistence.*;
@Entity
@Table(name = "tutorials")
public class Tutorial {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    @Column(name = "title")
    private String title;
    @Column(name = "description")
    private String description;
    @Column(name = "published")
    private boolean published;
    public Tutorial() {}
    public Tutorial(String title, String description, boolean published) {
        this.title = title;
        this.description = description;
        this.published = published;
    }
    // getters and setters
    @Override
    public String toString() {return "Tutorial [id=" + id + ", title=" + title + ", desc=" + description + ", published=" + published + "]";
    }
}
  • `@Entity` 注解指示该类是一个持久化的 Java 类。

  •  `@Table` 注解提供了映射到该实体的表名。

  • `@Id` 注解用于主键。

  • `@GeneratedValue` 注解用于定义主键的生成策略。GenerationType.AUTO 表示自动递增字段。

  • `@Column` 注解用于定义数据库中映射的列。

以上是 Tutorial 类的代码,它定义了数据模型的结构和属性,并提供了必要的方法和注解来与数据库进行映射和交互。

创建 JPA Repository

在 repository 包中,创建一个 TutorialRepository 接口,它扩展了 JpaRepository。这个存储库将与数据库中的 Tutorials 进行交互。

repository/TutorialRepository.java

package com.bezkoder.spring.data.jpa.test.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import com.bezkoder.spring.data.jpa.test.model.Tutorial;
public interface TutorialRepository extends JpaRepository {List findByPublished(boolean published);
  List findByTitleContaining(String title);
}

现在我们可以使用 JpaRepository 的方法:save()、findOne()、findById()、findAll()、count()、delete()、deleteById()…,而无需实现这些方法。

我们还定义了自定义的查找方法:

Spring Data JPA 会自动提供实现。

使用 @Query 注解进行自定义查询的示例:

Spring Boot 中的自定义查询示例:使用 Spring JPA @Query

你也可以修改这个存储库以支持分页,相关指南可以在下面找到:

– [Spring Boot 分页和过滤器示例 | Spring JPA,Pageable](https://www.bezkoder.com/spring-boot-pagination-filter-jpa-pageable/)

使用 @DataJpaTest 编写单元测试

在 src/test/java 下创建一个名为 JPAUnitTest 的类,继承它。我们将在这个类中测试多种情况(CRUD 操作、查找方法)。

package com.bezkoder.spring.data.jpa.test;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;
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 com.bezkoder.spring.data.jpa.test.model.Tutorial;
import com.bezkoder.spring.data.jpa.test.repository.TutorialRepository;

@DataJpaTest
public class JPAUnitTest {

  @Autowired
  private TestEntityManager entityManager;

  @Autowired
  TutorialRepository repository;

  @Test
  public void should_find_no_tutorials_if_repository_is_empty() {Iterable tutorials = repository.findAll();

    assertThat(tutorials).isEmpty();}

  @Test
  public void should_store_a_tutorial() {Tutorial tutorial = repository.save(new Tutorial("Tut title", "Tut desc", true));

    assertThat(tutorial).hasFieldOrPropertyWithValue("title", "Tut title");
    assertThat(tutorial).hasFieldOrPropertyWithValue("description", "Tut desc");
    assertThat(tutorial).hasFieldOrPropertyWithValue("published", true);
  }

  @Test
  public void should_find_all_tutorials() {Tutorial tut1 = new Tutorial("Tut#1", "Desc#1", true);
    entityManager.persist(tut1);

    Tutorial tut2 = new Tutorial("Tut#2", "Desc#2", false);
    entityManager.persist(tut2);

    Tutorial tut3 = new Tutorial("Tut#3", "Desc#3", true);
    entityManager.persist(tut3);

    Iterable tutorials = repository.findAll();

    assertThat(tutorials).hasSize(3).contains(tut1, tut2, tut3);
  }

  @Test
  public void should_find_tutorial_by_id() {Tutorial tut1 = new Tutorial("Tut#1", "Desc#1", true);
    entityManager.persist(tut1);

    Tutorial tut2 = new Tutorial("Tut#2", "Desc#2", false);
    entityManager.persist(tut2);

    Tutorial foundTutorial = repository.findById(tut2.getId()).get();

    assertThat(foundTutorial).isEqualTo(tut2);
  }

  @Test
  public void should_find_published_tutorials() {Tutorial tut1 = new Tutorial("Tut#1", "Desc#1", true);
    entityManager.persist(tut1);

    Tutorial tut2 = new Tutorial("Tut#2", "Desc#2", false);
    entityManager.persist(tut2);

    Tutorial tut3 = new Tutorial("Tut#3", "Desc#3", true);
    entityManager.persist(tut3);

    Iterable tutorials = repository.findByPublished(true);

    assertThat(tutorials).hasSize(2).contains(tut1, tut3);
  }

  @Test
  public void should_find_tutorials_by_title_containing_string() {Tutorial tut1 = new Tutorial("Spring Boot Tut#1", "Desc#1", true);
    entityManager.persist(tut1);

    Tutorial tut2 = new Tutorial("Java Tut#2", "Desc#2", false);
    entityManager.persist(tut2);

    Tutorial tut3 = new Tutorial("Spring Data JPA Tut#3", "Desc#3", true);
    entityManager.persist(tut3);

    Iterable tutorials = repository.findByTitleContaining("ring");

    assertThat(tutorials).hasSize(2).contains(tut1, tut3);
  }

  @Test
  public void should_update_tutorial_by_id() {Tutorial tut1 = new Tutorial("Tut#1", "Desc#1", true);
    entityManager.persist(tut1);

    Tutorial tut2 = new Tutorial("Tut#2", "Desc#2", false);
    entityManager.persist(tut2);

    Tutorial updatedTut = new Tutorial("updated Tut#2", "updated Desc#2", true);

    Tutorial tut = repository.findById(tut2.getId()).get();
    tut.setTitle(updatedTut.getTitle());
    tut.setDescription(updatedTut.getDescription());
    tut.setPublished(updatedTut.isPublished());
    repository.save(tut);

    Tutorial checkTut = repository.findById(tut2.getId()).get();
   
    assertThat(checkTut.getId()).isEqualTo(tut2.getId());
    assertThat(checkTut.getTitle()).isEqualTo(updatedTut.getTitle());
    assertThat(checkTut.getDescription()).isEqualTo(updatedTut.getDescription());
    assertThat(checkTut.isPublished()).isEqualTo(updatedTut.isPublished());
  }

  @Test
  public void should_delete_tutorial_by_id() {Tutorial tut1 = new Tutorial("Tut#1", "Desc#1", true);
    entityManager.persist(tut1);

    Tutorial tut2 = new Tutorial("Tut#2", "Desc#2", false);
    entityManager.persist(tut2);

    Tutorial tut3 = new Tutorial("Tut#3", "Desc#3", true);
    entityManager.persist(tut3);

    repository.deleteById(tut2.getId());

    Iterable tutorials = repository.findAll();

    assertThat(tutorials).hasSize(2).contains(tut1, tut3);
  }

  @Test
  public void should_delete_all_tutorials() {entityManager.persist(new Tutorial("Tut#1", "Desc#1", true));
    entityManager.persist(new Tutorial("Tut#2", "Desc#2", false));

    repository.deleteAll();

    assertThat(repository.findAll()).isEmpty();}
}

运行单元测试

[INFO] Tests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 7.901 s - in com.bezkoder.spr
ing.data.jpa.test.JPAUnitTest
...
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  32.594 s
[INFO] Finished at: 2020-05-07T20:48:47+07:00
[INFO] -------------

JUnit 测试结果

总结

今天我们使用 JUnit 5 和 @DataJPATest 以及 TestEntityManager 与 H2 数据库创建了一个针对 JPA Repository 的 Spring Boot 测试。我们还对许多 CRUD 操作和自定义查找方法进行了单元测试。 文章来源地址 https://www.toymoban.com/diary/java/699.html

到此这篇关于 @DataJpaTest 的 Spring Data Repository 单元测试示例的文章就介绍到这了, 更多相关内容可以在右上角搜索或继续浏览下面的相关文章,希望大家以后多多支持 TOY 模板网!

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于1970-01-01发表,共计10267字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)