如何在Java中Mock CompletableFuture?JUnit测试阻塞问题求助
异步任务JUnit测试阻塞问题与解决方案
场景与问题
业务代码中使用CompletableFuture.supplyAsync结合自定义ForkJoinPool执行异步任务,代码如下:
异步任务调用代码
CompletableFuture<Beneficiary> beneficiaryCompletableFuture = CompletableFuture.supplyAsync(() -> beneficiaryService.validateAndGetCustomer(dto.getBeneficiary()), forkJoinPool); CompletableFuture<List<Product>> productsCompletableFuture = CompletableFuture.supplyAsync(() -> productService.validateAndGetProducts(dto.getProductCodeList()), forkJoinPool);
ForkJoinPool配置类
@Configuration public class ForkJoinConfig { @Bean public ForkJoinPool getForkJoinPool(){ return new ForkJoinPool(); } }
在JUnit测试时调用出现阻塞,无法正常运行,需要实现Mock异步任务并区分Beneficiary、List<Product>两种不同类型的返回结果。
解决方案
1. Mock底层服务而非直接Mock CompletableFuture
无需直接MockCompletableFuture.supplyAsync,转而Mock依赖的beneficiaryService和productService,让它们直接返回已完成的CompletableFuture,这样异步任务会立即拿到结果,不会产生阻塞。
示例测试代码(基于Mockito + Spring Boot Test):
@SpringBootTest public class TargetServiceTest { @Autowired private TargetService targetService; // 包含上述异步代码的业务服务 @MockBean private BeneficiaryService beneficiaryService; @MockBean private ProductService productService; @Test void testAsyncBusinessLogic() { // 构造测试数据 Beneficiary mockBeneficiary = new Beneficiary(); List<Product> mockProducts = List.of(new Product()); BusinessDto testDto = new BusinessDto(); // 给testDto设置必要属性... // Mock服务方法,返回已完成的Future when(beneficiaryService.validateAndGetCustomer(any())) .thenReturn(CompletableFuture.completedFuture(mockBeneficiary)); when(productService.validateAndGetProducts(anyList())) .thenReturn(CompletableFuture.completedFuture(mockProducts)); // 执行待测试的业务方法 targetService.executeAsyncLogic(testDto); // 验证服务方法是否被正确调用(可选) verify(beneficiaryService).validateAndGetCustomer(any()); verify(productService).validateAndGetProducts(anyList()); } }
2. 替换测试用线程池(可选)
如果需要控制测试时的线程执行逻辑,可以用@MockBean替换配置中的ForkJoinPool,让异步任务在当前线程执行,避免异步阻塞:
@SpringBootTest public class TargetServiceTest { @MockBean private ForkJoinPool forkJoinPool; @BeforeEach void setUpThreadPoolMock() { // 让submit方法直接在当前线程执行任务 when(forkJoinPool.submit(any(Callable.class))) .thenAnswer(invocation -> { Callable<?> task = invocation.getArgument(0); return task.call(); }); } // 其余测试代码同上... }
3. 显式等待异步任务完成
如果必须执行真实的异步逻辑,测试时需要显式等待所有CompletableFuture完成后再断言结果:
@Test void testAsyncTasksWithWait() { // 准备测试数据和Mock逻辑... // 调用业务方法获取Future实例 CompletableFuture<Beneficiary> beneficiaryFuture = targetService.getBeneficiaryFuture(testDto); CompletableFuture<List<Product>> productsFuture = targetService.getProductsFuture(testDto); // 等待所有异步任务完成 CompletableFuture.allOf(beneficiaryFuture, productsFuture).join(); // 断言结果正确性 assertEquals(mockBeneficiary, beneficiaryFuture.get()); assertEquals(mockProducts, productsFuture.get()); }
内容的提问来源于stack exchange,提问作者Sérgio M.
相关产品推荐
相关产品推荐

