CompletableFuture Spy测试无限运行及Mockito类型错误求助
核心问题分析
1. Mockito WrongTypeOfReturnValue 错误
你使用when(spy.get()).thenThrow()语法对CompletableFuture的spy对象进行stub时,Mockito会先执行真实的get()方法。CompletableFuture内部实现中,get()的调用可能触发toString相关方法,导致Mockito在处理stub逻辑时出现类型不匹配——错误地尝试将PatientTreatmentDetails返回给原本应返回String的toString方法。根据报错提示,spy对象的stub必须优先使用doThrow/doReturn系列方法,避免执行真实方法带来的副作用。
2. 测试无限执行
业务代码先通过CompletableFuture.allOf().join()等待所有异步任务完成,再调用get()获取结果。如果spy的CompletableFuture被错误stub,会干扰其完成状态,导致join()无法正常结束;另外,当get()抛出InterruptedException后,代码调用Thread.currentThread().interrupt()中断当前线程,可能引发JUnit测试线程的异常处理逻辑,进而导致无限阻塞。
修复方案
方案1:用doThrow替换when...thenThrow(修正spy的stub方式)
修改测试中对CompletableFuture spy的stub逻辑,避免执行真实的get()方法:
@Test void interruptedExceptionThrownByCompletableFutures() throws InterruptedException, ExecutionException { // given when(patientsClient.getPatientTreatmentDetails(RPT_TOKEN, "123", CLINICAL_ID)) .thenReturn(createPatientTreatmentDetails()); // 使用doThrow stub spy的get方法,跳过真实方法执行 CompletableFuture<List<HDTreatmentOrder>> hdFuture = spy(CompletableFuture.completedFuture(Collections.emptyList())); doThrow(new InterruptedException()).when(hdFuture).get(); CompletableFuture<List<PDTreatmentOrder>> pdFuture = spy(CompletableFuture.completedFuture(Collections.emptyList())); doThrow(new InterruptedException()).when(pdFuture).get(); when(hdService.getData(any(), any())).thenReturn(hdFuture); when(pdService.getData(any(), any())).thenReturn(pdFuture); when(tDService.getData(RPT_TOKEN, "123", filter)) .thenReturn(CompletableFuture.completedFuture(new PatientTreatmentDetails())); // then assertThrows(PatientOrderException.class, () -> treatmentOrderService.getTreatmentOrders("123", filter, RPT_TOKEN)); }
方案2:直接Mock CompletableFuture(更简洁可靠)
无需spy真实的CompletableFuture,直接mock并stub所需方法,彻底避免真实对象的副作用:
@Test void interruptedExceptionThrownByCompletableFutures() throws InterruptedException, ExecutionException { // given when(patientsClient.getPatientTreatmentDetails(RPT_TOKEN, "123", CLINICAL_ID)) .thenReturn(createPatientTreatmentDetails()); // Mock CompletableFuture,stub get()抛出异常,join()正常返回(避免allOf().join()阻塞) CompletableFuture<List<HDTreatmentOrder>> hdFuture = mock(CompletableFuture.class); doThrow(new InterruptedException()).when(hdFuture).get(); doNothing().when(hdFuture).join(); CompletableFuture<List<PDTreatmentOrder>> pdFuture = mock(CompletableFuture.class); doThrow(new InterruptedException()).when(pdFuture).get(); doNothing().when(pdFuture).join(); when(hdService.getData(any(), any())).thenReturn(hdFuture); when(pdService.getData(any(), any())).thenReturn(pdFuture); when(tDService.getData(RPT_TOKEN, "123", filter)) .thenReturn(CompletableFuture.completedFuture(new PatientTreatmentDetails())); // then assertThrows(PatientOrderException.class, () -> treatmentOrderService.getTreatmentOrders("123", filter, RPT_TOKEN)); }
额外检查:Setup方法的潜在问题
报错栈显示错误发生在setup方法第84行,需检查该方法中是否存在类似的spy对象stub错误(比如错误地用when(spy.toString()).thenReturn(...)返回非String类型),同样需要将这类stub替换为doReturn语法。
验证修复
修改后单独运行测试用例,确认:
- Mockito的
WrongTypeOfReturnValue错误消失 - 测试用例正常结束,无无限阻塞
- 成功触发
PatientOrderException,符合断言预期
内容的提问来源于stack exchange,提问作者te queiro

