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
相关产品推荐
相关产品推荐

