Quarkus多个单元测试共享同一个PostgresSQLContainer

问题背景

小码在给项目功能添加单元测试之后,集中运行所有单元测试,为了不污染数据库数据,就在docker中初始化pg数据库,并且使用@QuarkusTestResource(value = PostgresTestResource.class, restrictToAnnotatedClass = true)这个注解进行数据隔离。但是这个注解每次只能隔离一个测试类,也就是说当同时运行多个单元测试类的时候就要在docker中初始化多个pg数据库,大大降低了速度。 下图为7个测试类的执行时间:

执行时间为2sec638ms。

改进方案

要使多个单元测试类共享同一个 PostgreSQLContainer,可以使用@QuarkusTestResource配置单例的PostgreSQLContainer实例。

第一步,将PostgresSQLContainer的生命周期管理改为单例,修改注解中PostgresTestResource类。

Java 复制代码
package net.ximatai.muyun.test.testcontainers;

import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
import org.testcontainers.containers.PostgreSQLContainer;

import java.util.Map;

public class PostgresTestResource implements QuarkusTestResourceLifecycleManager {

    private static final String POSTGRES_IMAGE = "postgres:17-alpine";

    // 使用静态变量确保所有测试类共享一个实例
    private static final PostgreSQLContainer<?> POSTGRES = new PostgreSQLContainer<>(POSTGRES_IMAGE);

    @Override
    public Map<String, String> start() {
        if (!POSTGRES.isRunning()) {
            POSTGRES.start();
        }
        return Map.of(
            "quarkus.datasource.jdbc.url", POSTGRES.getJdbcUrl(),
            "quarkus.datasource.username", POSTGRES.getUsername(),
            "quarkus.datasource.password", POSTGRES.getPassword()
        );
    }

    @Override
    public void stop() {
        // 不停止容器,让其他测试类可以继续使用
    }
}

第二步,删除注解中restrictToAnnotatedClass = true,让所有测试类共享这个 PostgresTestResource.

Java 复制代码
@QuarkusTest
@QuarkusTestResource(PostgresTestResource.class)
public class TestCreateDailyReport {
    // 测试代码保持不变
}

第三步,如果需要清理数据库,可以在测试之间进行数据清理

为了保证测试之间不会因为数据库残留数据而产生干扰,你可以在测试方法中或 @BeforeEach 方法中添加数据清理逻辑:

import org.junit.jupiter.api.BeforeEach;

@BeforeEach public void cleanDatabase() { // 使用 noticeController 或者直接通过 JDBC 执行清理操作 // 示例:清空数据库表 TestCreateDailyReport.deleteAll(); // 假设有这样的方法 }

第四步,确保容器的复用跨越多个测试类

Testcontainers 支持通过 reuse 属性在本地共享容器(需要在 ~/.testcontainers.properties 中配置,ios不需要)。如果你希望跨多个测试会话共享容器,可以启用 reuse:

ini 复制代码
// ~/.testcontainers.properties
testcontainers.reuse.enable=true

并在容器初始化时设置 withReuse(true):

Java 复制代码
private static final PostgreSQLContainer<?> POSTGRES = 
    new PostgreSQLContainer<>(POSTGRES_IMAGE).withReuse(true);

测试结果

经过以上改进,所有单元测试类将共享一个 PostgreSQL 容器,避免重复初始化,显著提高测试运行速度。同时,通过数据清理逻辑,确保测试之间的隔离性。7个单元测试的总运行时间为1sec175ms。

执行效率提升了125%!

补充

在 Quarkus 中,@QuarkusTestResource 注解用于指定一个测试资源类(如数据库或其他外部依赖)来为单元测试提供必要的支持。

@QuarkusTestResource 的作用 这个注解的主要功能是:

  1. 在运行测试之前,启动并初始化指定的测试资源。
  2. 在测试运行完成后,清理并关闭这些资源。

参数解释

value 参数

value 指定了一个实现了 QuarkusTestResourceLifecycleManager 接口的类。

例如,这里是 PostgresTestResource.class,它可能会负责启动一个 PostgreSQL 数据库实例,并提供测试环境所需的连接信息。

restrictToAnnotatedClass 参数

restrictToAnnotatedClass 设置为 true 时:

  • 测试资源 仅对当前注解了 @QuarkusTestResource 的测试类 可见。
  • 换句话说,如果你有多个测试类,但其中只有一个类(例如 TestClassA)注解了 @QuarkusTestResource(value = PostgresTestResource.class, restrictToAnnotatedClass = true),那么这个测试资源 只会在 TestClassA 中被加载和使用

restrictToAnnotatedClass = false(默认值)时:

  • 测试资源将会对所有使用 @QuarkusTest 注解的测试类可用,并在整个测试生命周期内加载。

使用场景

  • restrictToAnnotatedClass = true:

    • 当测试资源不需要被其他测试类共享,或者可能与其他测试类产生冲突时使用。
    • 有助于减少资源浪费,因为只会在需要的测试类中加载。
  • restrictToAnnotatedClass = false:

    • 当多个测试类需要共享同一个资源时使用。例如,共享一个数据库实例以测试多个不同的功能。

示例代码

less 复制代码
@QuarkusTest
@QuarkusTestResource(value = PostgresTestResource.class, restrictToAnnotatedClass = true)
public class TestClassA {
    // 测试逻辑
}

@QuarkusTest
public class TestClassB {
    // 不会使用 PostgresTestResource
}

在上面的例子中:

  • PostgresTestResource 仅会在 TestClassA 中被加载和使用。
  • TestClassB 不会受到影响。
相关推荐
H5css�海秀4 小时前
今天是自学大模型的第一天(sanjose)
后端·python·node.js·php
SuniaWang4 小时前
《Spring AI + 大模型全栈实战》学习手册系列 · 专题六:《Vue3 前端开发实战:打造企业级 RAG 问答界面》
java·前端·人工智能·spring boot·后端·spring·架构
韩立学长4 小时前
Springboot校园跑腿业务系统0b7amk02(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
sheji34164 小时前
【开题答辩全过程】以 基于springboot的扶贫系统为例,包含答辩的问题和答案
java·spring boot·后端
代码栈上的思考5 小时前
消息队列:内存与磁盘数据中心设计与实现
后端·spring
程序员小假6 小时前
我们来说一下 b+ 树与 b 树的区别
java·后端
Meepo_haha7 小时前
Spring Boot 条件注解:@ConditionalOnProperty 完全解析
java·spring boot·后端
sheji34167 小时前
【开题答辩全过程】以 基于springboot的房屋租赁系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
Victor3568 小时前
MongoDB(57)如何优化MongoDB的查询性能?
后端
Victor3568 小时前
MongoDB(58)如何使用索引优化查询?
后端