如何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属性做更精准的异常信息校验。
过往错误点说明
- 不要在测试中传入原生未完成的
CompletableFuture,必须传入你mock后的对象才能让stub逻辑生效 - mock
CompletableFuture时泛型要和业务逻辑匹配,你之前定义的CompletableFuture<List<String>>和业务需要返回的DefaultAsyncResultSet类型不匹配,会导致mock不生效 - 不需要在setUp方法里写try-catch处理
get()的异常,直接在测试方法上声明抛出即可
内容的提问来源于stack exchange,提问作者troyblerg
相关产品推荐
相关产品推荐

