新手求教:如何测试CompletableFuture.get()的中断与超时异常
测试CompletableFuture.get()的InterruptedException和TimeoutException
问题分析
你之前直接Mock CompletableFuture本身的思路走不通——CompletableFuture.allOf()会生成全新的Future实例,你Mock的对象根本不会被业务代码用到;而且failedFuture是模拟任务执行失败的场景,和超时、中断的触发逻辑完全不相关。
解决方案
1. 触发TimeoutException测试
要让get(30, TimeUnit.SECONDS)触发超时,核心是让allOf组合后的Future在30秒内无法完成。可以通过Mock客户端的post方法,让其返回一个永远阻塞或延迟超过超时时间的任务:
@Test void shouldTriggerTimeoutException() { // Mock clientA.post,返回一个永远不会完成的Future when(clientA.post(anyString(), any(), any(ClassA.class))) .thenReturn(new CompletableFuture<>()); // 同理处理clientB,或者只让其中一个阻塞即可触发整体超时 when(clientB.post(anyString(), any(), any(ClassB.class))) .thenReturn(new CompletableFuture<>()); // 执行你的业务方法 YourService service = new YourService(clientA, clientB); service.yourBusinessMethod(language, json, a, b); // 验证超时逻辑:比如可以检查future是否被cancel,或对应日志是否输出 }
也可以用延迟执行器模拟超长时间的任务:
when(clientA.post(anyString(), any(), any(ClassA.class))) .thenApplyAsync(res -> res, CompletableFuture.delayedExecutor(40, TimeUnit.SECONDS));
2. 触发InterruptedException测试
要触发中断异常,需要在get()处于阻塞等待状态时,中断当前线程:
@Test void shouldTriggerInterruptedException() throws InterruptedException { // 创建两个未完成的Future,让业务代码进入get()阻塞 CompletableFuture<ClassA> futureA = new CompletableFuture<>(); when(clientA.post(anyString(), any(), any(ClassA.class))).thenReturn(futureA); CompletableFuture<ClassB> futureB = new CompletableFuture<>(); when(clientB.post(anyString(), any(), any(ClassB.class))).thenReturn(futureB); // 用单独线程执行业务方法 Thread taskThread = new Thread(() -> { try { yourService.yourBusinessMethod(language, json, a, b); } catch (Exception ignored) {} }); taskThread.start(); // 等待一小会儿确保进入get()阻塞 Thread.sleep(500); // 中断线程触发异常 taskThread.interrupt(); taskThread.join(); // 验证逻辑:比如检查线程是否被重新中断,或对应错误日志是否输出 }
关键注意点
- 不要Mock CompletableFuture本身:
allOf()会生成新实例,Mock对象不会参与业务流程 - 优先Mock底层依赖(clientA、clientB):更贴近真实业务场景,也更容易控制任务的执行状态
内容的提问来源于stack exchange,提问作者Marsei
相关产品推荐
相关产品推荐

