You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过自定义注解配置TestContainer测试,无需使用抽象类

如何通过自定义注解配置TestContainer测试,无需使用抽象类

我完全理解你不想用抽象类的诉求——继承层级会给测试类带来不必要的约束,用自定义注解来统一配置才是更优雅的解决方案。针对你使用的Spring Boot 2.7.18版本(没有@ServiceContainer注解),我们可以通过自定义Spring测试监听器结合注解元数据来实现你的需求,下面是具体步骤:

第一步:编写TestContainer的测试执行监听器

创建一个Spring TestExecutionListener,负责启动/停止MariaDB容器,并自动注册数据源属性到Spring环境中:

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;

import java.util.Map;

public class MariaDbTestContainerExecutionListener extends AbstractTestExecutionListener {

    // 静态容器实例,确保所有测试类共享同一个容器,避免重复启动
    private static final MySQLContainer<?> MYSQL_CONTAINER = new MySQLContainer<>(
        DockerImageName.parse("library/mariadb")
            .asCompatibleSubstituteFor("mysql")
    );

    @Override
    public void beforeTestClass(TestContext testContext) throws Exception {
        super.beforeTestClass(testContext);
        // 启动容器
        MYSQL_CONTAINER.start();
        // 获取Spring上下文,注册动态数据源属性
        ConfigurableApplicationContext applicationContext = testContext.getApplicationContext();
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        MutablePropertySources propertySources = environment.getPropertySources();
        
        propertySources.addFirst(new MapPropertySource("testContainerProperties", Map.of(
            "spring.datasource.url", MYSQL_CONTAINER.getJdbcUrl(),
            "spring.datasource.username", MYSQL_CONTAINER.getUsername(),
            "spring.datasource.password", MYSQL_CONTAINER.getPassword()
        )));
    }

    @Override
    public void afterTestClass(TestContext testContext) throws Exception {
        super.afterTestClass(testContext);
        // 测试类执行完成后停止容器
        MYSQL_CONTAINER.stop();
    }
}

第二步:完善自定义测试注解@MyTestConfig

在你的自定义注解中,除了原来的Spring测试注解,还要添加@TestExecutionListeners来引入上面的监听器,同时设置合并模式保留默认的监听器:

import org.springframework.test.context.TestExecutionListeners;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.testcontainers.junit.jupiter.Testcontainers;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
@TestExecutionListeners(
    listeners = MariaDbTestContainerExecutionListener.class,
    mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS
)
public @interface MyTestConfig {
}

第三步:编写简化的测试类

现在你的测试类只需要标注@MyTestConfig即可,完全不需要再写容器定义和属性配置代码,和你最开始想要的简洁形式一致:

@MyTestConfig
class MyTests {
    @Autowired 
    SomeJpaRepo repo;

    @Test
    void findAll() {
        assertThatCode(repo::findAll).doesNotThrowAnyExceptions();
    }
}

额外说明

  • 静态的容器实例会让所有使用@MyTestConfig的测试类共享同一个MariaDB容器,大大减少测试启动时间;
  • 测试监听器会自动在测试类执行前启动容器、注册属性,执行完成后停止容器,完全不需要手动管理;
  • 这种方式完全避免了抽象类的继承约束,测试类的结构更清爽。

备注:内容来源于stack exchange,提问作者Archimedes Trajano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 11:04:33