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

如何Mock FutureTask.get(long, TimeUnit)方法以返回指定响应

解决FutureTask.get()在单元测试中阻塞的问题

问题场景

你的业务代码通过手动创建FutureTask并提交给ExecutorService执行,然后调用get(long, TimeUnit)获取结果:

// 创建FutureTask
FutureTask<Optional<Attestation>> attestationDbTask
        = new FutureTask<>(() -> attestationStore.get(imageInfo.getBucketKey()));
// 提交执行
executorService.execute(attestationDbTask);
// 获取结果
attestation = attestationDbTask.get(AttestationServiceLimits.READ_ATTESTATION_PDB_TIME_OUT, TimeUnit.MILLISECONDS)
                .orElseThrow(() -> generateNotFoundOrNotAuthorizedException(imageInfo + " in Attestation Store"));

单元测试中已MockattestationStore.get(anyString())返回Optional.empty(),但测试卡在attestationDbTask.get()方法处。

阻塞原因

测试中如果使用真实的ExecutorService,线程池可能未实际调度执行FutureTask,导致FutureTask始终处于未完成状态,get()方法会一直阻塞直到超时;如果直接尝试MockFutureTask,由于它是具体类而非接口,默认的Mock框架(如Mockito)无法直接Mock其get()方法(需借助PowerMock等工具)。

解决方案

方案1:重构代码,改用ExecutorService.submit()获取Future(推荐)

手动创建FutureTask的方式不利于测试,建议改用ExecutorService.submit(Callable)方法直接返回Future接口实例,这样可以轻松MockFuture的行为:

重构后的业务代码

// 直接通过submit提交Callable,获取Future实例
Future<Optional<Attestation>> attestationFuture = executorService.submit(
    () -> attestationStore.get(imageInfo.getBucketKey())
);
// 调用Future的get方法
attestation = attestationFuture.get(AttestationServiceLimits.READ_ATTESTATION_PDB_TIME_OUT, TimeUnit.MILLISECONDS)
                .orElseThrow(() -> generateNotFoundOrNotAuthorizedException(imageInfo + " in Attestation Store"));

对应的测试代码

// Mock Future接口
Future<Optional<Attestation>> mockFuture = Mockito.mock(Future.class);
// 让ExecutorService.submit返回mock的Future
when(executorService.submit(any(Callable.class))).thenReturn(mockFuture);
// 配置mockFuture的get方法返回预期结果
when(mockFuture.get(anyLong(), any(TimeUnit.class))).thenReturn(Optional.empty());

// 执行被测试方法后,get()会直接返回预设结果,不会阻塞

方案2:手动触发FutureTask完成(无需重构)

如果无法修改业务代码,可以在测试中手动调用FutureTask.run()方法,让任务在当前线程执行完成,这样get()方法就会立即返回结果:

测试代码示例

// 获取到业务代码中创建的attestationDbTask实例(可通过反射、依赖注入或暴露测试入口获取)
attestationDbTask.run(); // 手动执行任务,完成FutureTask的状态

// 此时调用get()会立即返回mock的Optional.empty(),不会阻塞
attestation = attestationDbTask.get(...).orElseThrow(...);

方案3:用PowerMock Mock FutureTask(不推荐)

如果必须保留手动创建FutureTask的逻辑,且无法手动触发执行,可以使用PowerMock来Mock具体类FutureTask的get()方法:

测试代码示例(需引入PowerMock依赖)

// 准备PowerMock测试环境
@RunWith(PowerMockRunner.class)
@PrepareForTest(FutureTask.class)
public class YourTestClass {

    @Test
    public void testYourMethod() throws Exception {
        // Mock FutureTask实例
        FutureTask<Optional<Attestation>> mockTask = PowerMock.createMock(FutureTask.class);
        // 配置get方法返回预期结果
        expect(mockTask.get(anyLong(), any(TimeUnit.class))).andReturn(Optional.empty());
        PowerMock.replay(mockTask);

        // 替换业务代码中的FutureTask实例(需通过反射或依赖注入)
        // ...

        // 执行测试逻辑,get()会返回预设结果
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 00:24:17