1、简介 {#1简介}
在使用 Spring Data JPA 进行数据持久化的 Spring Boot 应用中,测试与数据库交互的 Repository 至关重要。
本文将带你了解如何使用 Spring Boot 提供的 @DataJpaTest
注解和 JUnit 对 Spring Data JPA Repository 进行有效地测试。
2、了解 @DataJpaTest 和 Repository 类 {#2了解-datajpatest-和-repository-类}
本节主要介绍在 Spring Boot 应用中,@DataJpaTest
和 Repository
之间的交互。
2.1、@DataJpaTest {#21datajpatest}
@DataJpaTest
注解用于测试 Spring Boot 应用中的 JPA Repository。它是一个专门的测试注解,为测试持久层提供了一个最小的 Spring Context。该注解可与 @RunWith
和 @SpringBootTest
等其他测试注解结合使用。
此外,@DataJpaTest
的范围仅限于应用的 JPA Repository 层。它不会加载整个 Application Context,从而使测试更快、更集中。此注解还为测试 JPA 实体提供了预配置的 EntityManager
和 TestEntityManager
。
2.2、Repository {#22repository}
在 Spring Data JPA 中,Repository 是 JPA 实体之上的一个抽象层。它为执行 CRUD(创建、读取、更新、删除)操作和执行自定义查询提供了一组方法。这些 Repository 通常从 JpaRepository
等接口继承而来,负责处理与特定实体类型相关的数据库交互。
3、可选参数 {#3可选参数}
@DataJpaTest
有一些可选参数,可以用来定制测试环境。
3.1、properties {#31properties}
该参数允许指定将应用于测试上下文的 Spring Boot 配置属性。这对于调整数据库连接细节、事务行为或其他与测试需求相关的 application properties 等设置非常有用:
@DataJpaTest(properties = {
"spring.datasource.url=jdbc:h2:mem:testdb",
"spring.jpa.hibernate.ddl-auto=create-drop"
})
public class UserRepositoryTest {
// 测试方法
}
3.2、showSql {#32showsql}
这会为测试启用 SQL 日志,可以查看 Repository 方法执行的实际 SQL 查询。此外,这还有助于调试或理解 JPA 查询是如何翻译的。默认情况下,SQL 日志是启用的。可以通过将值设置为 false
关闭它:
@DataJpaTest(showSql = false)
public class UserRepositoryTest {
// 测试方法
}
3.3、includeFilters 和 excludeFilters {#33includefilters-和-excludefilters}
通过这些参数,可以在组件扫描过程中包含或排除特定组件。可以利用它们缩小扫描范围,并通过只关注相关组件来优化测试性能:
@DataJpaTest(includeFilters = @ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = UserRepository.class),
excludeFilters = @ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = SomeIrrelevantRepository.class))
public class UserRepositoryTest {
// 测试方法
}
4、主要功能 {#4主要功能}
在 Spring Boot 应用中测试 JPA Repository 时,@DataJpaTest
注解是一个非常方便的工具。
4.1、配置测试环境 {#41配置测试环境}
为 JPA Repository 建立适当的测试环境可能既费时又棘手。@DataJpaTest
提供了一个现成的测试环境,其中包括测试 JPA Repository 的基本组件,如 EntityManager
和 DataSource
。
该环境专为测试 JPA Repository 而设计。它能确保我们的 Repository 方法在测试事务的上下文中运行,并与 H2 等安全的内存数据库而不是生产数据库进行交互。
4.2、依赖注入 {#42依赖注入}
@DataJpaTest
简化了测试类中的依赖注入过程。Repository 和其他基本 Bean 会自动注入测试上下文。这种无缝集成使开发人员能够专注于编写简洁而有效的测试用例,而不必为显式的 Bean 装配而烦恼。
4.3、默认回滚 {#43默认回滚}
此外,保持测试的独立性和可靠性也至关重要。默认情况下,每个注解了 @DataJpaTest
的测试方法都会在事务边界内运行。这就确保了在测试结束时,对数据库所做的更改会自动回滚,不会影响到下一次测试。
5、配置 {#5配置}
要使用 @DataJpaTest
,需要在项目中添加 scope
为 test
(确保它不包含在生产构建中)的 spring-boot-starter-test
依赖。这个轻量级的依赖包含用于测试的 JUnit 等基本测试库。
5.1、在 pom.xml 中添加依赖 {#51在-pomxml-中添加依赖}
在 pom.xml
中添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-test</artifactId>
<scope>test</scope>
</dependency>
添加依赖后,就可以在测试中使用 @DataJpaTest
注解了。该注解设置了一个内存 H2 数据库并配置了 Spring Data JPA,使我们可以编写与 Repository 交互的测试。
5.2、创建实体类 {#52创建实体类}
创建 User
实体类,代表用户数据:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
// Getter、Setter
}
5.3、创建 Repository 接口 {#53创建-repository-接口}
定义 UserRepository
,它是 Spring Data JPA Repository 接口,用于管理用户实体:
public interface UserRepository extends JpaRepository<User, Long> {
// 添加所需的其他方法
}
通过继承 JpaRepository<User, Long>
,UserRepository
可以访问 Spring Data JPA 开箱即用的标准 CRUD 操作。
此外,还可以在该接口中定义自定义查询方法,以满足特定的数据访问检索需求,如 findByUsername()
:
public interface UserRepository extends JpaRepository<User, Long> {
// 通过用户名查找用户的自定义查询方法
User findByUsername(String username);
}
6、实现 Repository 测试 {#6实现-repository--测试}
使用 @DataJpaTest
注解测试应用的 Repository 层。该注解会建立一个内存 H2 数据库,并配置 Spring Data JPA。这样,就可以编写与 Repository 交互的测试了。
6.1、设置测试类 {#61设置测试类}
首先,用 @DataJpaTest
注解来设置测试类。该注解会扫描注解为 @Entity
的实体类 和 Spring Data JPA Repository 接口。这样可以确保只加载相关组件进行测试,从而提高测试的针对性和性能:
@DataJpaTest
public class UserRepositoryTest {
// 定义测试方法
}
要创建 Repository 测试用例,首先需要将要测试的 Repository 注入到测试类中。这可以使用 @Autowired
注解来完成:
@Autowired
private UserRepository userRepository;
6.2、测试生命周期管理 {#62测试生命周期管理}
在测试生命周期管理中,@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()
方法中,可以在每个测试方法执行前执行任何必要的设置操作。这可能包括初始化测试数据、设置模拟(Mock)对象或准备测试所需的资源。
相反,在使用 @AfterEach
进行注解的 tearDown()
方法中,可以在每个测试方法执行完毕后执行清理操作。这可能涉及重置测试期间所做的任何更改、释放资源或执行任何必要的清理任务,以将测试环境恢复到初始状态。
6.3、测试插入操作 {#63测试插入操作}
现在,可以编写与 JPA Repository 交互的测试方法。例如,我们可能想测试能否将新用户保存到数据库中。由于每次测试前都会自动保存用户,因此我们可以直接专注于测试与 JPA Repository 的交互:
@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
方法按预期运行。
6.4、测试更新操作 {#64测试更新操作}
创建一个测试用例来测试更新操作:
@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());
}
6.5、测试 findByUsername() 方法 {#65测试-findbyusername-方法}
测试 findByUsername()
自定义查询方法:
@Test
void givenUser_whenFindByUsernameCalled_thenUserIsFound() {
User foundUser = userRepository.findByUsername("testuser");
assertNotNull(foundUser);
assertEquals("testuser", foundUser.getUsername());
}
7、事务行为 {#7事务行为}
默认情况下,所有用 @DataJpaTest
注解的测试都在事务中执行。这意味着测试期间对数据库所做的任何更改都会在测试结束时回滚,确保数据库保持其原始状态。这种默认行为可防止测试之间的干扰和数据损坏,从而简化测试。
不过,在某些情况下,我们可能需要禁用默认的事务行为来测试某些场景。例如,可能需要在测试结束后保存测试结果。
在这种情况下,可以使用 @Transactional
注解和 propagation = propagation.NOT_SUPPORTED
来覆盖默认的事务回滚行为:
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class UserRepositoryIntegrationTest {
// 测试方法
}
或者,可以禁用单个测试方法的事务:
@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void testMyMethodWithoutTransactions() {
// 修改数据的操作
}
8、总结 {#8总结}
本文介绍了如何使用 @DataJpaTest
在 JUnit 中测试 JPA Repository。
总的来说,@DataJpaTest
是一个强大的注解,可用于测试 Spring Boot 应用中的 JPA Repository。它为测试持久层提供了一个集中的测试环境和预配置工具。通过使用 @DataJpaTest
,可以确保 JPA Repository 正常运行,而无需启动整个 Spring Context。
Ref:https://www.baeldung.com/junit-datajpatest-repository