为何我的单元测试中CompletableFuture始终无法结束?
问题描述
我正在为一个使用CompletableFuture.supplyAsync()的旧方法编写单元测试,方法逻辑不复杂,但执行join()时测试一直停不下来。我觉得supplyAsync()应该能快速返回结果,怀疑是taskExecutor配置错误?我是Spring和Java新手,求各位给点建议,谢谢。
业务代码
public Response getReponse(Request request) { CompletableFuture<String> vip = CompletableFuture.supplyAsync(() -> { if(StringUtils.isBlank(request.getAddress())){ return ""; } Request newRequest = Request.builder() .userId(request.getUserId()) .address(request.getZipCode()) .build(); Response newResult = getResult(newRequest); return ("12345".equals(newResult.getZipCode()) + ""); // 原代码缺少右括号,已修正 }, taskExecutor); final Response result = getResult(request); result.setVIP(vip.join()); // 原代码变量名写错,zipCode应为vip,已修正 return result; }
单元测试代码
@Mock private ThreadPoolTaskExecutor taskExecutor; @Test void getReponseTest(){ SomeService someService = new SomeService(Constants, logService, taskExecutor); final SomeService someServiceSpy = Mockito.spy(someService); final Request request = TestHelper.buildRequest(); final Response response = TestTestHelper.buildResponse(); doReturn(response).when(someServiceSpy).getResult(any(Request.class)); Response result = someServiceSpy.getReponse(request); // 原代码方法名和变量名有误,已修正 // assertion ... }
问题分析与解决建议
核心原因:Mock的线程池未执行任务
你用Mockito Mock了ThreadPoolTaskExecutor,但默认Mock对象不会主动执行传入的Runnable任务,导致CompletableFuture一直处于未完成状态,调用join()就会无限阻塞。解决方法一:让Mock线程池执行任务
在测试方法中添加Stub逻辑,手动执行提交的任务:doAnswer(invocation -> { Runnable task = invocation.getArgument(0); task.run(); // 在当前线程同步执行任务 return null; }).when(taskExecutor).execute(any(Runnable.class));这样
supplyAsync()提交的任务会被立即执行,CompletableFuture能正常完成,join()就不会再卡住。解决方法二:使用真实线程池(更贴近生产环境)
如果不需要Mock线程池,直接初始化一个简单的真实线程池:private ThreadPoolTaskExecutor taskExecutor; @BeforeEach void setUp() { taskExecutor = new ThreadPoolTaskExecutor(); taskExecutor.setCorePoolSize(2); taskExecutor.setMaxPoolSize(2); taskExecutor.initialize(); }这种方式适合测试业务逻辑本身,而非线程池的特定行为。
额外修正:业务代码中的笔误
原业务代码存在两处错误,会导致编译或运行异常:- 返回语句缺少右括号:
return ("12345".equals(newResult.getZipCode()) + "";应改为return ("12345".equals(newResult.getZipCode()) + ""); - 变量名错误:
result.setVIP(zipCode.join());中的zipCode应为前面定义的vip变量。
- 返回语句缺少右括号:
内容的提问来源于stack exchange,提问作者Meilan
相关产品推荐
相关产品推荐

