测试类执行完毕后容器Destroy方法未调用的解决方案咨询
我在测试中使用了嵌入式容器,包括Kafka、PostgreSQL、Minio等,依赖配置如下:
<dependency> <groupId>com.playtika.testcontainers</groupId> <artifactId>embedded-kafka</artifactId> </dependency> <dependency> <groupId>com.playtika.testcontainers</groupId> <artifactId>embedded-postgresql</artifactId> </dependency> <dependency> <groupId>com.playtika.testcontainers</groupId> <artifactId>embedded-mssqlserver</artifactId> </dependency> <dependency> <groupId>com.playtika.testcontainers</groupId> <artifactId>embedded-minio</artifactId> </dependency>
我通过自定义注解@EnableTestContainers启动测试类所需的容器,但测试完成后,Docker中的容器并未停止,而是一直运行到所有测试类执行完毕。
尝试添加配置embedded.containers.force-shutdown=true,但因Spring上下文的原因未生效,容器仍仅在所有测试运行结束后才关闭。
尝试添加@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS),该注解能在类执行后正常关闭容器,但会破坏@Nested测试的逻辑——我已有大量基于@Nested类编写的测试,它们依赖父类上下文。
现提出两个问题:
- 如何在不使用
@DirtiesContext的情况下,在测试类执行完毕后关闭容器,且不影响@Nested测试? - 如果必须使用
@DirtiesContext注解,如何让它仅作用于父类,不在嵌套类执行后清理上下文?
问题1:不使用@DirtiesContext实现容器按需关闭
方案1:自定义容器生命周期处理器
利用Playtika Testcontainers的扩展机制,自定义TestExecutionListener,在整个测试类(含所有@Nested子类)执行完成后手动触发容器关闭:
import org.springframework.test.context.TestContext; import org.springframework.test.context.TestExecutionListener; import com.playtika.testcontainers.spring.EmbeddedContainersContextCustomizer; public class EmbeddedContainerShutdownListener implements TestExecutionListener { @Override public void afterTestClass(TestContext testContext) throws Exception { EmbeddedContainersContextCustomizer customizer = testContext.getApplicationContext() .getBean(EmbeddedContainersContextCustomizer.class); customizer.stop(); } }
在测试父类上注册该监听器:
@TestExecutionListeners(listeners = EmbeddedContainerShutdownListener.class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS) @EnableTestContainers public abstract class BaseIntegrationTest { // 父类通用逻辑 }
afterTestClass方法会在所有嵌套测试执行完毕后触发,既保证容器及时关闭,又不破坏嵌套类对父类上下文的依赖。
方案2:调整Playtika容器配置
通过配置让容器绑定测试类生命周期,而非全局Spring上下文:
在application-test.properties中添加:
embedded.containers.per-test-container=true embedded.containers.shutdown-on-context-close=true
该配置会为每个测试类创建独立容器实例,测试类(含所有嵌套子类)执行完毕后,容器随类级资源释放自动关闭,嵌套类可正常复用父类容器。
问题2:使用@DirtiesContext但不影响@Nested测试
方案1:父类设置@DirtiesContext并指定层级模式
在父类添加注解时,设置hierarchyMode = DirtiesContext.HierarchyMode.EXHAUSTIVE,确保仅在整个测试类层级(含所有嵌套类)执行完毕后才清理上下文:
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS, hierarchyMode = DirtiesContext.HierarchyMode.EXHAUSTIVE) @EnableTestContainers public abstract class BaseIntegrationTest { // 父类通用逻辑 }
EXHAUSTIVE模式会忽略嵌套类的上下文脏标记,仅在父类生命周期结束时触发清理。
方案2:嵌套类覆盖@DirtiesContext配置
在每个@Nested类上添加@DirtiesContext(classMode = DirtiesContext.ClassMode.NEVER),明确禁止嵌套类触发上下文清理:
@Nested @DirtiesContext(classMode = DirtiesContext.ClassMode.NEVER) class NestedTest { // 嵌套测试逻辑 }
这种方式无需修改父类配置,直接在已有嵌套类上补充注解即可保留父类的AFTER_CLASS清理逻辑。
内容的提问来源于stack exchange,提问作者Andrew Bessonov

