集成测试中如何Mock CompletableFuture.join()抛出异常
搞定CompletableFuture.join()的Mock异常问题
问题出在哪
你现在的Mock写法有两个关键问题:
- Mock的对象根本没被用到:你用
@MockBean创建的CompletableFuture实例,和业务代码里callCarrierPost返回的CompletableFuture.completedFuture()完全不是同一个对象,业务逻辑处理的是后者,所以你的Mock等于白写。 - 异常Mock的方式错了:你用
thenReturn返回一个异常对象,但join()需要的是抛出异常,而且CompletableFuture.join()抛出的是**CompletionException**,实际异常会被包在这个异常的cause里,不是直接返回异常对象。
正确的解决办法
根据你的代码逻辑,有两种靠谱的Mock方式:
方法1:让callCarrierPost返回带异常的Future
直接Mock业务类的callCarrierPost方法,让它返回一个已经处于异常状态的CompletableFuture,这样调用join()时会自动抛出CompletionException。
@Autowired private YourService yourService; // 替换成你的业务类名称 @Test void testProviderTimeoutCase() { // 创建一个失败的Future,包裹SocketTimeoutException CompletableFuture<ResponseEntity<Object>> failedFuture = new CompletableFuture<>(); failedFuture.completeExceptionally( new ResourceAccessException("I/O error on /uri", new SocketTimeoutException("Read Timeout")) ); // Mock callCarrierPost方法返回这个异常Future when(yourService.callCarrierPost(any(ServiceConfig.class), any(Class.class), any(ExecutionContext.class), any(AdapterContext.class))) .thenReturn(failedFuture); // 构造Future列表并处理 List<CompletableFuture<ResponseEntity<Object>>> futureList = Collections.singletonList(failedFuture); List<ResponseEntity<Object>> result = yourService.processResponseFutureList(futureList); // 验证结果:应该返回SERVICE_UNAVAILABLE的错误响应 assertThat(result.get(0).getStatusCode()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); }
方法2:Mock底层的carrierInvoker抛出异常
因为callCarrierPost里是同步调用carrierInvoker.postForObject后返回completedFuture,所以直接MockcarrierInvoker抛出异常,这样callCarrierPost返回的Future自然就带异常了。
@MockBean private CarrierInvoker carrierInvoker; // 你的carrierInvoker类 @Autowired private YourService yourService; @Test void testHttpErrorCase() { // Mock carrierInvoker抛出HttpStatusCodeException when(carrierInvoker.postForObject(any(ServiceConfig.class), any(Class.class), any(ExecutionContext.class), any(AdapterContext.class))) .thenThrow(new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Bad Request")); // 调用方法获取Future(传入对应参数) CompletableFuture<ResponseEntity<Object>> future = yourService.callCarrierPost(new ServiceConfig(), Object.class, new ExecutionContext(), new AdapterContext()); // 处理Future列表 List<ResponseEntity<Object>> result = yourService.processResponseFutureList(Collections.singletonList(future)); // 验证结果:返回BAD_REQUEST的响应 assertThat(result.get(0).getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); }
重要提醒
CompletableFuture.completeExceptionally(Throwable)是让Future进入异常状态的正确方式,调用join()时会自动抛出包裹了该异常的CompletionException,正好匹配你processResponseFutureList里的异常处理逻辑。- 别想着直接Mock
CompletableFuture的join()方法,因为CompletableFuture是final类(JDK8及以上),Mockito默认Mock不了final方法,就算开了额外配置也没必要,上面两种方法更直接简单。
内容的提问来源于stack exchange,提问作者J Harish
相关产品推荐
相关产品推荐

