@DataJpaTest and Repository Class in JUnit – JUnit 中的 @DataJpaTest 和存储库类

最后修改: 2024年 2月 28日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.导言

When working with Spring Boot applications that utilize Spring Data JPA for data persistence, it’s crucial to test the repositories that interact with the database. In this tutorial, we’ll explore how to effectively test Spring Data JPA repositories using the @DataJpaTest annotation provided by Spring Boot along with JUnit.

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

2. Understanding @DataJpaTest and Repository Class

2.了解 @DataJpaTest 和资源库类

In this section, we’ll delve into the interaction between @DataJpaTest and class repositories within the context of Spring Boot applications.

在本节中,我们将深入探讨 Spring Boot 应用程序中 @DataJpaTest 和类库之间的交互。

2.1. @DataJpaTest

2.1. @DataJpaTest.

The @DataJpaTest annotation is used to test JPA repositories in Spring Boot applications. It’s a specialized test annotation that provides a minimal Spring context for testing the persistence layer. This annotation can be used in conjunction with other testing annotations like @RunWith and @SpringBootTest.

@DataJpaTest注解用于测试 Spring Boot 应用程序中的 JPA 存储库。它是一个专门的测试注解,可为测试持久层提供最小的 Spring 上下文。 该注解可与 @RunWith@SpringBootTest 等其他测试注解结合使用。

In addition, the scope of @DataJpaTest is limited to the JPA repository layer of the application. It doesn’t load the entire application context, which can make testing faster and more focused. This annotation also provides a pre-configured EntityManager and TestEntityManager for testing JPA entities.

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

2.2. Repository Class

2.2.存储库类

In Spring Data JPA, repositories serve as an abstraction layer on top of JPA entities. It provides a set of methods for performing CRUD (Create, Read, Update, Delete) operations and executing custom queries. These repositories typically extend from interfaces like JpaRepository and are responsible for handling database interactions related to specific entity types.

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

3. Optional Parameters

3.可选参数

@DataJpaTest does have some optional parameters we can use to customize the test environment.

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

3.1. properties

3.1.属性</em

This parameter allows us to specify Spring Boot configuration properties that will be applied to our test context. This can be useful for adjusting settings like database connection details, transaction behavior, or other application properties relevant to our testing needs:

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

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

3.2. showSql

3.2.showSql

This enables SQL logging for our tests and allows us to see the actual SQL queries executed by the repository methods. Moreover, this can help debug or understand how the JPA queries are translated. By default, the SQL logging is enabled. We can turn it off by setting the value to false:

这将为我们的测试启用 SQL 日志,并允许我们查看存储库方法执行的实际 SQL 查询。此外,这还有助于调试或理解 JPA 查询是如何翻译的d。默认情况下,SQL 日志是启用的。我们可以通过将值设置为false来关闭它:

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

3.3. includeFilters and excludeFilters

3.3.includeFiltersexcludeFilters

These parameters enable us to include or exclude specific components during component scanning. We can use them to narrow down the scanning scope and optimize test performance by focusing only on the relevant components:

这些参数使我们能够在组件扫描过程中包含或排除特定组件。我们可以使用它们缩小扫描范围,并通过只关注相关组件来优化测试性能

@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
}

4. Key Features

4.主要特点

When it comes to testing JPA repositories in Spring Boot applications, the @DataJpaTest annotation can be a handy tool. Let’s explore its key features and benefits in detail.

当涉及到在 Spring Boot 应用程序中测试 JPA 资源库时,@DataJpaTest 注解是一个非常方便的工具。让我们详细了解一下它的主要功能和优势。

4.1. Test Environment Configuration

4.1.测试环境配置

Setting up a proper test environment for JPA repositories can be time-consuming and tricky. @DataJpaTest provides a ready-made testing environment that includes essential components for testing JPA repositories, such as the EntityManager and DataSource.

为 JPA 存储库建立适当的测试环境可能既耗时又棘手。@DataJpaTest提供了一个现成的测试环境,其中包括用于测试 JPA 存储库的基本组件,例如 EntityManagerDataSource

This environment is specifically designed for testing JPA repositories. It ensures that our repository methods run within the context of a test transaction, interacting with a safe, in-memory database like H2 instead of the production database.

该环境专为测试 JPA 存储库而设计。它能确保我们的存储库方法在测试事务的上下文中运行,并与 H2 等安全的内存数据库而不是生产数据库进行交互。

4.2. Dependency Injection

4.2.依赖注入

@DataJpaTest simplifies the process of dependency injection within our test classes. Repositories, along with other essential beans, are automatically injected into the test context. This seamless integration enables developers to focus on writing concise and effective test cases without the hassle of explicit bean wiring.

@DataJpaTest简化了测试类中的依赖注入过程。资源库和其他基本 Bean 将自动注入到测试上下文中。这种无缝集成使开发人员能够专注于编写简洁而有效的测试用例,而无需为显式的 Bean 布线而烦恼。

4.3. Rollback by Default

4.3.默认回滚

Moreover, keeping tests independent and reliable is crucial. By default, each test method annotated with @DataJpaTest runs within a transactional boundary. This ensures that changes made to the database are automatically rolled back at the end of the test, leaving a clean slate for the next test.

此外,保持测试的独立性和可靠性也至关重要。默认情况下,使用 @DataJpaTest 进行注释的每个测试方法都会在事务边界内运行。这将确保在测试结束时自动回滚对数据库所做的更改,为下一次测试留下一片净土。

5. Configuration and Setup

5.配置和设置

To use @DataJpaTest, we need to add the spring-boot-starter-test dependency to our project with scope “test“. This lightweight dependency includes essential testing libraries like JUnit for testing, ensuring it’s not included in our production build.

要使用 @DataJpaTest,我们需要在项目中添加 spring-boot-starter-test 依赖关系,其作用域为”test“。这个轻量级的依赖关系包括用于测试的 JUnit 等基本测试库,确保它不会包含在我们的生产构建中。

5.1. Adding Dependency to pom.xml

5.1.将依赖关系添加到 pom.xml

Let’s add the following dependency to the pom.xml:

让我们将以下依赖关系添加到 pom.xml 中:

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

Once we’ve added the dependency, we can use the @DataJpaTest annotation in our tests. This annotation sets up an in-memory H2 database and configure Spring Data JPA for us, allowing us to write tests that interact with our repository classes.

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

5.2. Creating the Entity Class

5.2.创建实体类

Now, let’s create the User entity class, which will represent user data:

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

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

5.3. Creating the Repository Interface

5.3.创建版本库接口

Next, we define the UserRepository, a Spring Data JPA repository interface for managing User entities:

接下来,我们定义 UserRepository – 用于管理 User 实体的 Spring Data JPA 资源库接口:

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

By extending JpaRepository<User, Long>, our UserRepository gains access to standard CRUD operations provided by Spring Data JPA out-of-the-box.

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

Additionally, we can define custom query methods within this interface to suit specific data access retrieval needs, such as findByUsername():

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

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

6. Implementing Repository Tests

6.实施资源库测试

To test the repository layer of our application, we’ll utilize the @DataJpaTest annotation. By using this annotation, an in-memory H2 database will be set up, and Spring Data JPA will be configured. This allows us to write tests that interact with our repository classes.

要测试应用程序的存储库层,我们将使用 @DataJpaTest 注解。通过使用该注解,我们将建立一个内存 H2 数据库,并配置 Spring Data JPA。这样,我们就可以编写与存储库类交互的测试。

6.1. Setting up the Test Class

6.1.设置测试类

To begin, let’s set up the test class by annotating it with @DataJpaTest. This annotation scans for entity classes annotated with @Entity and Spring Data JPA repositories interfaces. This ensures that only relevant components are loaded for testing, improving test focus and performance:

首先,让我们用 @DataJpaTest 对测试类进行注解,从而建立测试类。该注解会扫描使用 @Entity 和 Spring Data JPA 资源库接口注解的实体类。这可确保只加载相关组件进行测试,从而提高测试的针对性和性能:

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

To create a repository test case, we first need to inject the repository that we want to test into our test class. This can be done using the @Autowired annotation:

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

@Autowired
private UserRepository userRepository;

6.2. Test Lifecycle Management

6.2.测试生命周期管理

In the context of test lifecycle management, @BeforeEach and @AfterEach annotations are used to perform setup and teardown operations before and after each test method, respectively. This ensures that each test method runs in a clean and isolated environment, with consistent initial conditions and cleanup procedures.

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

Here’s how we can incorporate test lifecycle management into our test class:

下面是我们如何将测试生命周期管理纳入测试类的方法:

@BeforeEach
public void setUp() {
    // Initialize test data before each test method
    testUser = new User();
    testUser.setUsername("testuser");
    testUser.setPassword("password");
    userRepository.save(testUser);
}

@AfterEach
public void tearDown() {
    // Release test data after each test method
    userRepository.delete(testUser);
}

In the setUp() method annotated with @BeforeEach, we can perform any necessary setup operations required before each test method execution. This might include initializing test data, setting up mock objects, or preparing resources needed for the test.

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

Conversely, in the tearDown() method annotated with @AfterEach, we can perform cleanup operations after each test method execution. This might involve resetting any changes made during the test, releasing resources, or performing any necessary cleanup tasks to restore the test environment to its original state.

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

6.3. Testing the Insertion Operation

6.3.测试插入操作

Now, we can write test methods that interact with the JPA repository. For example, we might want to test that we can save a new user to the database. Since a user is automatically saved before each test, we can directly focus on testing interactions with the JPA repository:

现在,我们可以编写与 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());
}

If we observe the console log for the test case, we’ll notice the following logs:

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

Began transaction (1) for test context  
.....

Rolled back transaction for test:  

These logs indicate that the @BeforeEach and @AfterEach methods are functioning as expected.

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

6.4. Testing the Update Operation

6.4.测试更新操作

In addition, we can create a test case for testing the update operation:

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

@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. Testing the findByUsername() Method

6.5.测试 findByUsername() 方法

Now, let’s test the findByUsername() custom query method we created:

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

@Test
void givenUser_whenFindByUsernameCalled_thenUserIsFound() {
    User foundUser = userRepository.findByUsername("testuser");

    assertNotNull(foundUser);
    assertEquals("testuser", foundUser.getUsername());
}

7. Transactional Behavior

7.事务行为

By default, all tests annotated with @DataJpaTest are executed within a transaction. This means that any changes made to the database during the test are rolled back at the end of the test, ensuring that the database is left in its original state. This default behavior simplifies testing by preventing interference between tests and data corruption.

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

However, there may be cases where we need to disable transactional behavior to test certain scenarios. For instance, testing results may need to persist beyond the test.

不过,在某些情况下,我们可能需要禁用事务行为来测试某些场景。例如,测试结果可能需要在测试结束后继续存在。

In such a case, we can disable transactions for a specific test class using the @Transactional annotation with propagation = propagation.NOT_SUPPORTED:

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

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

Or we can disable transactions for an individual test method:

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

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

8. Conclusion

8.结论

In this article, we learned how to use @DataJpaTest to test our JPA repository in JUnit. Overall, @DataJpaTest is a powerful annotation for testing JPA repositories in Spring Boot applications. It provides a focused testing environment and pre-configured tools for testing persistence layers. By using @DataJpaTest, we can ensure that our JPA repositories are functioning correctly without having to start up the entire Spring context.

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

As always, the source code for the examples is available over on GitHub.

与往常一样,这些示例的源代码可在 GitHub 上获取。