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

如何mock CompletableFuture.get() 解决单元测试无限卡死问题

解决方案

问题根因

你测试卡死的核心原因有两个:

  • 你最初的测试用例传入了未完成状态的原生CompletableFuture,get()方法会一直阻塞直到future进入完成状态,自然会无限卡死
  • 后续你虽然写了mock逻辑,但没有把mock后的CompletableFuture对象传入processRows2方法调用,mock完全没有生效,还有泛型、返回值类型不匹配的问题也会导致mock失效

修正后的测试实现

首先确保你引入了Mockito的测试支持,JUnit4在类上加@RunWith(MockitoJUnitRunner.class),JUnit5加@ExtendWith(MockitoExtension.class)。

成功分支测试

@RunWith(MockitoJUnitRunner.class)
public class FeatureDaoImplTest {
    private FeatureDaoImpl featureDao;

    @Mock
    private CompletableFuture<DefaultAsyncResultSet> mockFuture;
    @Mock
    private DefaultAsyncResultSet mockResultSet;
    @Mock
    private Row mockRow;
    // connection如果是依赖也可以用@Mock注入

    @Before
    public void setUp() {
        // 初始化目标测试类,connection按你实际的依赖注入方式处理
        featureDao = new FeatureDaoImpl(connection);
    }

    @Test
    public void testProcessRows2_Success() throws Exception {
        // 模拟ResultSet返回的Row列表
        List<Row> expectedRows = Collections.singletonList(mockRow);
        // 配置ResultSet的currentPage返回模拟数据
        Mockito.when(mockResultSet.currentPage()).thenReturn(expectedRows);
        // 配置Future的get方法返回模拟的ResultSet
        Mockito.when(mockFuture.get()).thenReturn(mockResultSet);

        // 调用目标方法,传入mock好的Future
        List<Row> actualRows = featureDao.processRows2(mockFuture);

        // 结果断言
        Assert.assertEquals(expectedRows.size(), actualRows.size());
        Assert.assertEquals(expectedRows.get(0), actualRows.get(0));
    }
}

注:直接在测试方法声明上加上throws Exception即可规避get()方法的未处理异常提示,不需要额外写try-catch。

异常分支测试

要覆盖ExecutionException和InterruptedException两个异常场景,写法如下:

@Test(expected = RuntimeException.class)
public void testProcessRows2_ExecutionException() throws Exception {
    // 模拟get方法抛出ExecutionException
    Mockito.when(mockFuture.get()).thenThrow(new ExecutionException("模拟执行失败", new Throwable()));
    featureDao.processRows2(mockFuture);
}

@Test(expected = RuntimeException.class)
public void testProcessRows2_InterruptedException() throws Exception {
    // 模拟get方法抛出InterruptedException
    Mockito.when(mockFuture.get()).thenThrow(new InterruptedException("模拟线程中断"));
    featureDao.processRows2(mockFuture);
}

JUnit5可以用Assertions.assertThrows代替expected属性做更精准的异常信息校验。

过往错误点说明

  1. 不要在测试中传入原生未完成的CompletableFuture,必须传入你mock后的对象才能让stub逻辑生效
  2. mockCompletableFuture时泛型要和业务逻辑匹配,你之前定义的CompletableFuture<List<String>>和业务需要返回的DefaultAsyncResultSet类型不匹配,会导致mock不生效
  3. 不需要在setUp方法里写try-catch处理get()的异常,直接在测试方法上声明抛出即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 00:30:01