JUnit测试在CompletableFuture.allOf().join()处无限阻塞,如何解决?
解决CompletableFuture测试卡住问题及正确JUnit写法
你的测试卡在CompletableFuture.allOf(...).join()处,核心原因是:你模拟的supplyAsync返回的Future未处于已完成状态,或者静态Mock逻辑存在问题,导致join()一直阻塞等待Future完成。
以下是正确的测试编写步骤和代码示例:
1. 核心修复点
- 确保模拟
supplyAsync时返回已完成的CompletableFuture实例,这样join()会立即返回,不会阻塞。 - 正确使用Mockito的静态Mock,用
try-with-resources控制作用域,避免污染其他测试。 - 精确匹配方法参数,区分两个不同类型的
supplyAsync调用(如果需要)。
2. 完整测试代码
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class TestSample { @Mock Executor executor; @Mock SomeClass someClass; // Sample类依赖的someClass需要Mock @Mock Obj1 mockObj1; @Mock Obj2 mockObj2; @Mock Request mockRequest; @Test void testSampleMethod() { // 创建两个已完成的Future,对应两个supplyAsync的返回值 CompletableFuture<Obj1> completedFuture1 = CompletableFuture.completedFuture(mockObj1); CompletableFuture<Obj2> completedFuture2 = CompletableFuture.completedFuture(mockObj2); // 静态Mock CompletableFuture,用try-with-resources自动关闭Mock try (MockedStatic<CompletableFuture> cfMock = mockedStatic(CompletableFuture.class)) { // 匹配supplyAsync调用,根据传入的Supplier返回对应已完成Future cfMock.when(() -> CompletableFuture.supplyAsync(any(Supplier.class), any(Executor.class))) .thenAnswer(invocation -> { Supplier<?> supplier = invocation.getArgument(0); // 可以根据supplier的行为或返回类型区分,这里简单模拟两次不同返回 if (supplier.get() instanceof Obj1) { return completedFuture1; } else { return completedFuture2; } }); // 初始化Sample实例并注入Mock依赖 Sample sample = new Sample(); sample.executor = executor; sample.someClass = someClass; // 执行测试方法 sample.sampleMethod(mockRequest); // 验证supplyAsync被调用两次 cfMock.verify(() -> CompletableFuture.supplyAsync(any(Supplier.class), any(Executor.class)), times(2)); } } }
3. 关键注意事项
- 拼写修正:你的Sample代码中
CompletableFuture.SupplyAsync是拼写错误,实际API是小写s的supplyAsync,必须修正,否则真实代码会报错。 - Mock作用域:静态Mock必须用
try-with-resources包裹,否则会影响其他测试用例的静态方法行为。 - Executor的Mock:因为我们已经Mock了
supplyAsync的返回值,所以Mock的Executor不会实际执行任务,无需额外配置它的执行逻辑。 - 依赖注入:确保Sample类中的
executor、someClass等依赖被正确注入Mock对象,避免空指针或调用真实业务逻辑。
4. 原测试卡住的具体原因
如果你的completedFuture未通过CompletableFuture.completedFuture(...)初始化,或者静态Mock未正确生效,那么supplyAsync会调用真实方法:而你Mock的Executor没有实际执行任务的能力,导致Future一直处于未完成状态,join()就会无限阻塞。
内容的提问来源于stack exchange,提问作者sk001
相关产品推荐
相关产品推荐

