Spring Boot项目Testcontainers异常启动两个容器测试失败如何解决
问题根因
你使用的Testcontainers JDBC自动启动模式,会在每次解析带tc:前缀的JDBC URL时触发容器创建。Spring Boot的初始化流程中,Flyway自动配置会先加载并解析JDBC URL创建第一个容器,执行迁移脚本;之后Hikari连接池初始化时会再次解析同个JDBC URL,触发第二个容器的创建,第一个容器随之被销毁,最终第二个容器没有执行Flyway脚本导致测试失败。
解决方案
方案1:添加容器可重用配置(改动最小)
在现有JDBC URL后追加&TC_REUSABLE=true参数,同时在本地~/.testcontainers.properties文件中添加配置testcontainers.reuse.enable=true,多次解析同个JDBC URL时会复用已启动的容器,不会重复创建。
修改后的数据源配置如下:
spring.flyway.locations = classpath:database/structure,classpath:database/data spring.datasource.url=jdbc:tc:postgresql:13.3:///databasename?TC_INITSCRIPT=file:src/test/resources/database/dataset/add_user.sql&TC_REUSABLE=true
方案2:显式声明容器实例(稳定性更高,推荐)
放弃JDBC URL自动启动的方式,在抽象测试类中显式声明静态PostgreSQL容器,配合Spring动态属性注入绑定数据源,全程只会启动一个容器。
- 引入Testcontainers JUnit 5集成依赖:
<!-- Maven示例 --> <dependency> <groupId>org.testcontainers</groupId> <artifactId>junit-jupiter</artifactId> <version>1.15.3</version> <scope>test</scope> </dependency>
- 修改抽象测试类代码:
@ExtendWith({RestDocumentationExtension.class, SpringExtension.class}) @TestPropertySource(locations = "classpath:application-test.properties") @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @Testcontainers public abstract class AbstractIntegrationTest { // 静态容器全局仅初始化一次 static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:13.3") .withInitScript("database/dataset/add_user.sql") .withReuse(true); // 动态注入数据源配置到Spring上下文 @DynamicPropertySource static void registerPgProperties(DynamicPropertyRegistry registry) { registry.add("spring.datasource.url", postgres::getJdbcUrl); registry.add("spring.datasource.username", postgres::getUsername); registry.add("spring.datasource.password", postgres::getPassword); } //... }
- 删除
application-test.properties中原有的带tc:前缀的spring.datasource.url配置。
方案3:对齐Flyway与主数据源配置
在application-test.properties中添加配置,强制Flyway复用主数据源的配置,避免Flyway单独触发容器创建:
spring.flyway.url=${spring.datasource.url} spring.flyway.user=${spring.datasource.username} spring.flyway.password=${spring.datasource.password}
内容的提问来源于stack exchange,提问作者jel
相关产品推荐
相关产品推荐

