You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 20:01:14