如何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_
相关产品推荐
相关产品推荐

