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

SpringBoot测试中Testcontainers的MSSQL容器无法自动启动问题咨询

SpringBoot测试中Testcontainers的MSSQL容器无法自动启动问题咨询

问题描述

我写了这样一个Spring Boot测试类:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@RunWith(SpringRunner.class)
@Testcontainers
@ContextConfiguration(initializers = OperationCodeRepositoryTest.DockerDatasourceInitializer.class)
public class OperationCodeRepositoryTest {

    @Container
    private static MSSQLServerContainer<?> container = new MSSQLServerContainer<>(
        "mcr.microsoft.com/mssql/server:2019-latest").acceptLicense()
        .withInitScript("database/init.sql");

    @Autowired
    private OperationCodeRepository repository;

    public static class DockerDatasourceInitializer
    implements ApplicationContextInitializer<ConfigurableApplicationContext> {

        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
                applicationContext, "spring.datasource.url=" + container.getJdbcUrl(),
                "spring.datasource.username=" + container.getUsername(),
                "spring.datasource.password=" + container.getPassword());
        }
    }
}

运行测试时总是报错:

java.lang.IllegalStateException: Mapped port can only be obtained after the container is started

我原本以为加了@Container注解容器就会自动启动,现在只能手动在@BeforeClass里调用start()方法才能解决:

@BeforeClass
public static void init() {
    try{
        container.start();
    } catch(Exception e){
        e.printStackTrace();
    }
}

想请教下我哪里操作错了?谢谢!


问题原因

核心问题出在初始化时机不匹配:

  • 你自定义的DockerDatasourceInitializer属于ApplicationContextInitializer,它的执行时机是Spring上下文初始化的早期阶段,这时候Testcontainers还没来得及启动你的MSSQL容器。
  • 当你在初始化器里调用container.getJdbcUrl()时,容器还没启动,自然拿不到映射的端口,所以抛出了那个异常。
  • @Container注解本身确实会自动启动容器,但它的启动时机是在Spring上下文初始化之后、测试方法执行之前,晚于你的初始化器执行时间。

解决方案

推荐使用Testcontainers官方针对Spring Boot提供的@DynamicPropertySource注解来替代自定义初始化器,这能完美解决时机问题:

修改后的测试类代码:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@RunWith(SpringRunner.class)
@Testcontainers
public class OperationCodeRepositoryTest {

    @Container
    private static MSSQLServerContainer<?> container = new MSSQLServerContainer<>(
            "mcr.microsoft.com/mssql/server:2019-latest")
            .acceptLicense()
            .withInitScript("database/init.sql");

    @Autowired
    private OperationCodeRepository repository;

    @DynamicPropertySource
    static void configureDatabaseProperties(DynamicPropertyRegistry registry) {
        // 容器启动后才会调用这些方法获取属性
        registry.add("spring.datasource.url", container::getJdbcUrl);
        registry.add("spring.datasource.username", container::getUsername);
        registry.add("spring.datasource.password", container::getPassword);
    }

    // 你的测试方法...
}

为什么这样可行?

@DynamicPropertySource的执行时机是在Testcontainers启动容器之后、Spring上下文完成属性加载之前,这时候容器已经完全启动,能正确获取到映射的端口和连接信息,不会再抛出端口未就绪的异常。

另外还要确保你的依赖版本是兼容的:

  • 确保引入了org.testcontainers:mssqlserver和org.testcontainers:jdbc依赖
  • Spring Boot版本建议2.4+,Testcontainers版本建议1.15+,这样@DynamicPropertySource能正常工作

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 07:44:08