问题背景
小码在给项目功能添加单元测试之后,集中运行所有单元测试,为了不污染数据库数据,就在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
的作用 这个注解的主要功能是:
- 在运行测试之前,启动并初始化指定的测试资源。
- 在测试运行完成后,清理并关闭这些资源。
参数解释
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
不会受到影响。
版权归原作者 Java进阶八股文 所有, 如有侵权,请联系我们删除。