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

JUnit测试在CompletableFuture.allOf().join()处无限阻塞,如何解决?

解决CompletableFuture测试卡住问题及正确JUnit写法

你的测试卡在CompletableFuture.allOf(...).join()处,核心原因是:你模拟的supplyAsync返回的Future未处于已完成状态,或者静态Mock逻辑存在问题,导致join()一直阻塞等待Future完成。

以下是正确的测试编写步骤和代码示例:

1. 核心修复点

  • 确保模拟supplyAsync时返回已完成的CompletableFuture实例,这样join()会立即返回,不会阻塞。
  • 正确使用Mockito的静态Mock,用try-with-resources控制作用域,避免污染其他测试。
  • 精确匹配方法参数,区分两个不同类型的supplyAsync调用(如果需要)。

2. 完整测试代码

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class TestSample {

    @Mock
    Executor executor;
    @Mock
    SomeClass someClass; // Sample类依赖的someClass需要Mock
    @Mock
    Obj1 mockObj1;
    @Mock
    Obj2 mockObj2;
    @Mock
    Request mockRequest;

    @Test
    void testSampleMethod() {
        // 创建两个已完成的Future,对应两个supplyAsync的返回值
        CompletableFuture<Obj1> completedFuture1 = CompletableFuture.completedFuture(mockObj1);
        CompletableFuture<Obj2> completedFuture2 = CompletableFuture.completedFuture(mockObj2);

        // 静态Mock CompletableFuture,用try-with-resources自动关闭Mock
        try (MockedStatic<CompletableFuture> cfMock = mockedStatic(CompletableFuture.class)) {
            // 匹配supplyAsync调用,根据传入的Supplier返回对应已完成Future
            cfMock.when(() -> CompletableFuture.supplyAsync(any(Supplier.class), any(Executor.class)))
                    .thenAnswer(invocation -> {
                        Supplier<?> supplier = invocation.getArgument(0);
                        // 可以根据supplier的行为或返回类型区分,这里简单模拟两次不同返回
                        if (supplier.get() instanceof Obj1) {
                            return completedFuture1;
                        } else {
                            return completedFuture2;
                        }
                    });

            // 初始化Sample实例并注入Mock依赖
            Sample sample = new Sample();
            sample.executor = executor;
            sample.someClass = someClass;

            // 执行测试方法
            sample.sampleMethod(mockRequest);

            // 验证supplyAsync被调用两次
            cfMock.verify(() -> CompletableFuture.supplyAsync(any(Supplier.class), any(Executor.class)), times(2));
        }
    }
}

3. 关键注意事项

  • 拼写修正:你的Sample代码中CompletableFuture.SupplyAsync是拼写错误,实际API是小写s的supplyAsync,必须修正,否则真实代码会报错。
  • Mock作用域:静态Mock必须用try-with-resources包裹,否则会影响其他测试用例的静态方法行为。
  • Executor的Mock:因为我们已经Mock了supplyAsync的返回值,所以Mock的Executor不会实际执行任务,无需额外配置它的执行逻辑。
  • 依赖注入:确保Sample类中的executor、someClass等依赖被正确注入Mock对象,避免空指针或调用真实业务逻辑。

4. 原测试卡住的具体原因

如果你的completedFuture未通过CompletableFuture.completedFuture(...)初始化,或者静态Mock未正确生效,那么supplyAsync会调用真实方法:而你Mock的Executor没有实际执行任务的能力,导致Future一直处于未完成状态,join()就会无限阻塞。

内容的提问来源于stack exchange,提问作者sk001

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 00:31:07