SpringBoot中ApplicationReadyEvent的E2E测试环境准备问题
问题描述
我正尝试为监听ApplicationReadyEvent的组件编写E2E测试,需要在事件触发前用LocalStack搭建基础设施。但测试运行时,ApplicationReadyEvent会在beforeEach及测试方法执行前就触发,导致我无法完成环境准备或mock操作,求解决方案。
业务类代码
@Component public class ApplicationStartup implements ApplicationListener<ApplicationReadyEvent> { private final MyService myService; public ApplicationStartup(MyService myService) { this.myService = myService; } @Override public void onApplicationEvent(final ApplicationReadyEvent event) { myService.myMethod(); } }
测试类代码
@SpringBootTest @Testcontainers class ApplicationStartupE2E { @Container static LocalStackContainer localStack = LocalStackConstants.createLocalStackContainer(); @SpyBean private MyService myService; @DynamicPropertySource static void overrideProperties(DynamicPropertyRegistry registry) { registry.add("amazon.aws.region", () -> localStack.getRegion()); registry.add("amazon.aws.access-key", () -> localStack.getAccessKey()); registry.add("amazon.aws.secret-key", () -> localStack.getSecretKey()); registry.add("amazon.s3.endpoint", () -> localStack.getEndpointOverride(LocalStackContainer.Service.S3).toString()); registry.add("amazon.dynamodb.endpoint", () -> localStack.getEndpointOverride(LocalStackContainer.Service.DYNAMODB).toString()); } @BeforeEach void setUp() { LocalStackFunctions.doStuff(localStack); } @AfterEach void tearDown() { LocalStackFunctions.doStuff(localStack); } @Test void shouldTriggerProcess() { // Additional mocks if needed // Trigger event // Assert } }
解决思路
思路1:加开关控制,手动触发启动逻辑
给业务类加个配置开关,默认开启自动执行,测试时关闭,等环境和mock都准备好后再手动触发启动逻辑。
修改业务类:
@Component public class ApplicationStartup implements ApplicationListener<ApplicationReadyEvent> { private final MyService myService; @Value("${application.startup.enabled:true}") private boolean enabled; public ApplicationStartup(MyService myService) { this.myService = myService; } @Override public void onApplicationEvent(final ApplicationReadyEvent event) { if (enabled) { myService.myMethod(); } } // 暴露手动触发的方法 public void triggerStartup() { myService.myMethod(); } }
修改测试类:
@SpringBootTest(properties = "application.startup.enabled=false") @Testcontainers class ApplicationStartupE2E { @Container static LocalStackContainer localStack = LocalStackConstants.createLocalStackContainer(); @SpyBean private MyService myService; @Autowired private ApplicationStartup applicationStartup; @DynamicPropertySource static void overrideProperties(DynamicPropertyRegistry registry) { registry.add("amazon.aws.region", () -> localStack.getRegion()); registry.add("amazon.aws.access-key", () -> localStack.getAccessKey()); registry.add("amazon.aws.secret-key", () -> localStack.getSecretKey()); registry.add("amazon.s3.endpoint", () -> localStack.getEndpointOverride(LocalStackContainer.Service.S3).toString()); registry.add("amazon.dynamodb.endpoint", () -> localStack.getEndpointOverride(LocalStackContainer.Service.DYNAMODB).toString()); } @BeforeEach void setUp() { LocalStackFunctions.doStuff(localStack); // 这里可以放心做mock操作 doNothing().when(myService).someDependencyMethod(); } @AfterEach void tearDown() { LocalStackFunctions.doStuff(localStack); } @Test void shouldTriggerProcess() { // 环境和mock都准备好后,手动触发启动逻辑 applicationStartup.triggerStartup(); // 验证方法被调用 verify(myService, times(1)).myMethod(); } }
思路2:测试时替换监听器,延迟执行
在测试里用@TestConfiguration替换原有的ApplicationStartup组件,让它不自动执行逻辑,等测试准备就绪后再手动触发真实的启动逻辑。
修改测试类:
@SpringBootTest @Testcontainers class ApplicationStartupE2E { @Container static LocalStackContainer localStack = LocalStackConstants.createLocalStackContainer(); @SpyBean private MyService myService; @Autowired private ApplicationContext context; @DynamicPropertySource static void overrideProperties(DynamicPropertyRegistry registry) { registry.add("amazon.aws.region", () -> localStack.getRegion()); registry.add("amazon.aws.access-key", () -> localStack.getAccessKey()); registry.add("amazon.aws.secret-key", () -> localStack.getSecretKey()); registry.add("amazon.s3.endpoint", () -> localStack.getEndpointOverride(LocalStackContainer.Service.S3).toString()); registry.add("amazon.dynamodb.endpoint", () -> localStack.getEndpointOverride(LocalStackContainer.Service.DYNAMODB).toString()); } // 自定义测试配置,替换原组件 @TestConfiguration static class TestConfig { @Bean @Primary public ApplicationStartup applicationStartup(MyService myService) { // 返回一个空实现的监听器,不自动执行逻辑 return new ApplicationStartup(myService) { @Override public void onApplicationEvent(ApplicationReadyEvent event) { // 空实现 } }; } } @BeforeEach void setUp() { LocalStackFunctions.doStuff(localStack); // 这里做mock } @Test void shouldTriggerProcess() { // 创建真实的监听器实例 ApplicationStartup realStartup = new ApplicationStartup(myService); // 手动触发事件 realStartup.onApplicationEvent(new ApplicationReadyEvent(context)); // 断言方法被调用 verify(myService, times(1)).myMethod(); } }
思路3:提前完成LocalStack准备
如果不想修改业务代码,就把LocalStack的准备工作提前到Spring上下文启动前,用@BeforeAll代替@BeforeEach,确保环境在ApplicationReadyEvent触发前就配置完成。
修改测试类:
@SpringBootTest @Testcontainers class ApplicationStartupE2E { @Container static LocalStackContainer localStack = LocalStackConstants.createLocalStackContainer(); @SpyBean private MyService myService; // 提前在Spring上下文启动前完成LocalStack配置 @BeforeAll static void prepareLocalStack() { LocalStackFunctions.doStuff(localStack); } @DynamicPropertySource static void overrideProperties(DynamicPropertyRegistry registry) { registry.add("amazon.aws.region", () -> localStack.getRegion()); registry.add("amazon.aws.access-key", () -> localStack.getAccessKey()); registry.add("amazon.aws.secret-key", () -> localStack.getSecretKey()); registry.add("amazon.s3.endpoint", () -> localStack.getEndpointOverride(LocalStackContainer.Service.S3).toString()); registry.add("amazon.dynamodb.endpoint", () -> localStack.getEndpointOverride(LocalStackContainer.Service.DYNAMODB).toString()); } @AfterAll static void cleanUpLocalStack() { LocalStackFunctions.doStuff(localStack); } @Test void shouldTriggerProcess() { // 此时ApplicationReadyEvent已经触发,直接验证 verify(myService, times(1)).myMethod(); } }
内容的提问来源于stack exchange,提问作者magalenyo
相关产品推荐
相关产品推荐

