SpringBoot中@DataJpaTest 和 JUnit 中的存储库类

当使用使用Spring Data JPA进行数据持久化的 Spring Boot 应用程序时,测试与数据库交互的存储库至关重要。在本教程中,我们将探索如何使用Spring Boot 和JUnit提供的@DataJpaTest注释来有效地测试 Spring Data JPA 存储库。

@DataJpaTest是什么?
@DataJpaTest  注解用于测试 Spring Boot 应用程序中的 JPA 存储库。它是一个专门的测试注释,为测试持久层提供了最小的 Spring 上下文。此注释可以与其他测试注释(例如@RunWith 和 @SpringBootTest )结合使用 。

此外,@DataJpaTest的范围仅限于应用程序的 JPA 存储库层。它不会加载整个应用程序上下文,这可以使测试更快、更集中。 此注释还提供了用于测试 JPA 实体的预配置EntityManager 和 TestEntityManager 。

存储库Repository 类
在 Spring Data JPA 中,存储库充当 JPA 实体之上的抽象层。它提供了一组用于执行 CRUD(创建、读取、更新、删除)操作和执行自定义查询的方法。这些存储库通常从JpaRepository等接口扩展,负责处理与特定实体类型相关的数据库交互。

@DataJpaTest参数
@DataJpaTest确实有一些可选参数,我们可以使用它们来自定义测试环境。

1. 特性
此参数允许我们指定将应用于我们的测试上下文的 Spring Boot 配置属性。这对于调整数据库连接详细信息、事务行为或与我们的测试需求相关的其他应用程序属性等设置很有用:

@DataJpaTest(properties = {
    "spring.datasource.url=jdbc:h2:mem:testdb",
   
"spring.jpa.hibernate.ddl-auto=create-drop"
})
public class UserRepositoryTest {
   
// ... test methods
}

2. 显示Sql
这为我们的测试启用了 SQL 日志记录,并允许我们查看存储库方法执行的实际 SQL 查询。此外,这可以帮助调试或理解 JPA 查询是如何转换的。默认情况下,SQL 日志记录处于启用状态。我们可以通过将值设置为false来关闭它:

@DataJpaTest(showSql = false)
public class UserRepositoryTest {
    // ... test methods
}

3. 包含过滤器和排除过滤器
这些参数使我们能够在组件扫描期间包含或排除特定组件。我们可以使用它们来缩小扫描范围并通过仅关注相关组件来优化测试性能:

@DataJpaTest(includeFilters = @ComponentScan.Filter(
    type = FilterType.ASSIGNABLE_TYPE, 
    classes = UserRepository.class),
  excludeFilters = @ComponentScan.Filter(
    type = FilterType.ASSIGNABLE_TYPE, 
    classes = SomeIrrelevantRepository.class))
public class UserRepositoryTest {
    // ... test methods
}

主要特点

  • 测试环境配置:为 JPA 存储库设置适当的测试环境可能既耗时又棘手。@DataJpaTest提供了一个现成的测试环境,其中包括用于测试 JPA 存储库的基本组件,例如EntityManager和DataSource。该环境是专门为测试 JPA 存储库而设计的。它确保我们的存储库方法在测试事务的上下文中运行,与安全的内存数据库(如 H2)而不是生产数据库进行交互。
  • 依赖注入:@DataJpaTest简化了我们的测试类中的依赖注入过程。存储库以及其他重要的 bean 会自动注入到测试上下文中。这种无缝集成使开发人员能够专注于编写简洁有效的测试用例,而无需进行显式 bean 连接的麻烦。
  • 默认回滚:此外,保持测试的独立性和可靠性至关重要。默认情况下,每个用@DataJpaTest注释的测试方法在事务边界内运行。这可确保对数据库所做的更改在测试结束时自动回滚,为下一次测试留下干净的记录。

配置和设置
要使用@DataJpaTest,我们需要将spring-boot-starter-test 依赖项添加到范围为“ test ”的项目中。这种轻量级依赖项包括用于测试的 JUnit 等基本测试库,确保它不包含在我们的生产构建中。

将以下依赖项添加到pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-test</artifactId>
    <scope>test</scope>
</dependency>

添加依赖项后,我们可以在测试中使用@DataJpaTest注释。该注释设置了一个内存中的 H2 数据库并为我们配置 Spring Data JPA,允许我们编写与存储库类交互的测试。

创建实体类
现在,让我们创建User实体类,它将表示用户数据:

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String password;
    
    // getters and setters
}

创建存储库接口
接下来,我们定义UserRepository,一个用于管理User实体的 Spring Data JPA 存储库接口:

public interface UserRepository extends JpaRepository<User, Long> {
    // Add custom methods if needed
}

通过扩展JpaRepository<User, Long>,我们的UserRepository可以访问 Spring Data JPA 开箱即用提供的标准 CRUD 操作。

此外,我们可以在此接口中定义自定义查询方法来满足特定的数据访问检索需求,例如findByUsername():

public interface UserRepository extends JpaRepository<User, Long> {
    // Custom query method to find a user by username
    User findByUsername(String username);


实施存储库测试
为了测试应用程序的存储库层,我们将利用@DataJpaTest注释。通过使用这个注解,将建立一个内存H2数据库,并配置Spring Data JPA。这允许我们编写与存储库类交互的测试。

1、设置测试类
首先,让我们通过使用@DataJpaTest注释来设置测试类。此注释扫描使用@Entity和 Spring Data JPA 存储库接口注释的实体类。这可确保仅加载相关组件进行测试,从而提高测试重点和​​性能:

@DataJpaTest
public class UserRepositoryTest {
    // Add test methods here
}

要创建存储库测试用例,我们首先需要将要测试的存储库注入到测试类中。这可以使用@Autowired注释来完成 :

@Autowired
private UserRepository userRepository;

2、测试生命周期管理
在测试生命周期管理的上下文中,@BeforeEach和@AfterEach注释分别用于在每个测试方法之前和之后执行设置和拆卸操作。这确保了每个测试方法在干净且隔离的环境中运行,具有一致的初始条件和清理程序。

以下是我们如何将测试生命周期管理合并到我们的测试类中:

@BeforeEach
public void setUp() {
    // 在每个测试方法之前初始化测试数据
    testUser = new User();
    testUser.setUsername(
"testuser");
    testUser.setPassword(
"password");
    userRepository.save(testUser);
}
@AfterEach
public void tearDown() {
   
// 在每种测试方法之后发布测试数据
    userRepository.delete(testUser);
}

在使用@BeforeEach注解的setUp()方法中,我们可以在每个测试方法执行之前执行所需的任何必要的设置操作。这可能包括初始化测试数据、设置模拟对象或准备测试所需的资源。

相反,在用@AfterEach注释的tearDown()方法中,我们可以在每个测试方法执行后执行清理操作。这可能涉及重置测试期间所做的任何更改、释放资源或执行任何必要的清理任务以将测试环境恢复到其原始状态。

3、测试插入操作
现在,我们可以编写与 JPA 存储库交互的测试方法。例如,我们可能想测试是否可以将新用户保存到数据库中。由于在每次测试之前都会自动保存用户,因此我们可以直接专注于测试与 JPA 存储库的交互:

@Test
void givenUser_whenSaved_thenCanBeFoundById() {
    User savedUser = userRepository.findById(testUser.getId()).orElse(null);
    assertNotNull(savedUser);
    assertEquals(testUser.getUsername(), savedUser.getUsername());
    assertEquals(testUser.getPassword(), savedUser.getPassword());
}

如果我们观察测试用例的控制台日志,我们会注意到以下日志:

Began transaction (1) for test context  
.....
Rolled back transaction for test:  

这些日志表明@BeforeEach和@AfterEach方法正在按预期运行。

4. 测试更新操作
另外,我们可以创建一个测试用例来测试更新操作:

@Test
void givenUser_whenUpdated_thenCanBeFoundByIdWithUpdatedData() {
    testUser.setUsername("updatedUsername");
    userRepository.save(testUser);
    User updatedUser = userRepository.findById(testUser.getId()).orElse(null);
    assertNotNull(updatedUser);
    assertEquals(
"updatedUsername", updatedUser.getUsername());
}

5. 测试findByUsername()方法
现在,让我们测试我们创建的findByUsername()自定义查询方法:

@Test
void givenUser_whenFindByUsernameCalled_thenUserIsFound() {
    User foundUser = userRepository.findByUsername("testuser");
    assertNotNull(foundUser);
    assertEquals(
"testuser", foundUser.getUsername());
}

事务行为
默认情况下,所有用@DataJpaTest注释的测试都在事务中执行。这意味着测试期间对数据库所做的任何更改都会在测试结束时回滚,以确保数据库保持其原始状态。此默认行为通过防止测试之间的干扰和数据损坏来简化测试。

但是,在某些情况下,我们可能需要禁用事务行为来测试某些场景。例如,测试结果可能需要在测试之外持续存在。

在这种情况下,我们可以使用@Transactional注释和propagation= propagation.NOT_SUPPORTED禁用特定测试类的事务:

@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class UserRepositoryIntegrationTest {
    // ... test methods
}

或者我们可以禁用单个测试方法的事务:

@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void testMyMethodWithoutTransactions() {
    // ... code that modifies the database


结论
在本文中,我们学习了如何使用@DataJpaTest来测试JUnit中的 JPA 存储库。总的来说,@DataJpaTest是一个强大的注释,用于在 Spring Boot 应用程序中测试 JPA 存储库。它提供了一个集中的测试环境和用于测试持久层的预配置工具。通过使用@DataJpaTest,我们可以确保我们的 JPA 存储库正常运行,而无需启动整个 Spring 上下文。