如何测试带@Async的@ApplicationModuleListener异常抛出场景
测试异步事件监听器异常的几种方案
方案一:测试时禁用异步执行
直接在测试类上添加@DisableAsync注解,Spring会忽略方法上的@Async配置,让监听器同步执行,异常就能直接抛到测试方法中,原测试代码无需大幅修改:
@SpringBootTest @DisableAsync class MyServiceTest { @Autowired MyService myService; @Test void testExceptionThrow() { Event event = new Event("someUnexpectedValue"); assertThrows(UnexpectedValueException.class, () -> myService.onEvent(event)); } }
方案二:利用Spring事件发布机制+异步异常捕获
既然是事件监听器,更贴合实际场景的测试方式是通过Spring容器发布事件,而非直接调用方法。同时自定义异步异常处理器来捕获异常,测试时验证异常是否被正确捕获:
- 自定义测试用的异步异常处理器:
@Component public class TestAsyncExceptionHandler implements AsyncUncaughtExceptionHandler { private Throwable caughtException; @Override public void handleUncaughtException(Throwable ex, Method method, Object... params) { this.caughtException = ex; } public Throwable getCaughtException() { return caughtException; } public void reset() { caughtException = null; } }
- 配置测试用的异步任务执行器:
@Configuration @EnableAsync public class TestAsyncConfig implements AsyncConfigurer { private final TestAsyncExceptionHandler exceptionHandler; public TestAsyncConfig(TestAsyncExceptionHandler exceptionHandler) { this.exceptionHandler = exceptionHandler; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return exceptionHandler; } }
- 测试类中发布事件并验证异常:
@SpringBootTest class MyServiceTest { @Autowired ApplicationContext applicationContext; @Autowired TestAsyncExceptionHandler exceptionHandler; @BeforeEach void setUp() { exceptionHandler.reset(); } @Test void testExceptionThrow() throws InterruptedException { // 发布事件触发监听器 applicationContext.publishEvent(new Event("someUnexpectedValue")); // 等待异步任务执行完成(根据实际情况调整等待时间) Thread.sleep(1000); // 断言捕获到预期异常 assertNotNull(exceptionHandler.getCaughtException()); assertTrue(exceptionHandler.getCaughtException() instanceof UnexpectedValueException); assertEquals("fail!", exceptionHandler.getCaughtException().getMessage()); } }
方案三:修改监听器方法返回CompletableFuture(需调整业务代码)
如果允许修改业务代码,可以将void返回的监听器方法改为返回CompletableFuture<Void>,这样测试时可以通过get()方法获取异步执行的异常:
业务代码修改:
@Service public class MyService { ... @ApplicationModuleListener @Async public CompletableFuture<Void> onEvent(Event event) { if(event.getSomeField().equals(someExpectedValue)) { // 执行预期逻辑 return CompletableFuture.completedFuture(null); } else { return CompletableFuture.failedFuture(new UnexpectedValueException("fail!")); } } }
测试代码:
@SpringBootTest class MyServiceTest { @Autowired MyService myService; @Test void testExceptionThrow() throws ExecutionException, InterruptedException { Event event = new Event("someUnexpectedValue"); CompletableFuture<Void> future = myService.onEvent(event); // 调用get()会抛出执行时的异常 assertThrows(UnexpectedValueException.class, future::get); } }
内容的提问来源于stack exchange,提问作者jakub_k
相关产品推荐
相关产品推荐

