JUnit测试在CompletableFuture.allOf().join()处无限阻塞的解决方法
解决CompletableFuture.allOf().join()在JUnit测试中阻塞的问题
问题根源
你Mock了Executor但未让它实际执行提交的异步任务,导致future1和future2始终处于未完成状态,join()因此无限阻塞。以下是三种可行的测试实现方案:
方案1:使用同步执行的Executor
直接替换Mock的Executor为同步执行任务的实现,提交的Supplier会立刻在当前线程执行,Future快速完成,不会触发阻塞。
测试代码示例:
@ExtendWith(MockitoExtension.class) class SampleTest { @InjectMocks private Sample sample; @Mock private SomeClass1 someClass1; @Mock private SomeClass someClass; @Mock private SomeClass2 someClass2; @BeforeEach void setUp() { // 创建同步Executor:提交任务时立即执行 Executor syncExecutor = Runnable::run; // 给Sample的executor字段赋值 ReflectionTestUtils.setField(sample, "executor", syncExecutor); // Mock依赖方法的返回值 when(someClass1.someMethod()).thenReturn(new Obj1()); when(someClass.someMethod(any(Obj1.class))).thenReturn(new Obj1()); when(someClass2.someMethod()).thenReturn(new Obj2()); when(someClass.someMethod(any(Obj2.class))).thenReturn(new Obj2()); } @Test void testSampleMethod() { // 执行测试方法,无阻塞 sample.SampleMethod(new Request()); // 验证业务逻辑,比如someClass的方法调用次数 verify(someClass, times(2)).someMethod(any()); } }
方案2:Mock Executor时手动触发任务执行
如果必须Mock Executor,可借助Mockito的Answer捕获提交的任务,手动执行以确保Future完成。
测试代码示例:
@ExtendWith(MockitoExtension.class) class SampleTest { @InjectMocks private Sample sample; @Mock private Executor executor; @Mock private SomeClass1 someClass1; @Mock private SomeClass someClass; @Mock private SomeClass2 someClass2; @BeforeEach void setUp() { // Mock Executor的execute方法,手动执行提交的任务 doAnswer(invocation -> { Runnable task = invocation.getArgument(0); task.run(); // 立即执行异步任务 return null; }).when(executor).execute(any(Runnable.class)); // Mock依赖方法返回值 when(someClass1.someMethod()).thenReturn(new Obj1()); when(someClass.someMethod(any(Obj1.class))).thenReturn(new Obj1()); when(someClass2.someMethod()).thenReturn(new Obj2()); when(someClass.someMethod(any(Obj2.class))).thenReturn(new Obj2()); } @Test void testSampleMethod() { sample.SampleMethod(new Request()); verify(someClass, times(2)).someMethod(any()); } }
方案3:强制完成CompletableFuture(特殊场景用)
如果无需测试异步执行逻辑,仅想跳过阻塞,可通过反射强制完成Future,但这种方式会破坏原有代码逻辑,仅适用于极端场景,不推荐常规使用。
额外提示
你的Sample类代码存在一处小错误:future2的SupplyAsync指定了Supplier<Obj1>,但实际应匹配返回值类型Obj2,建议修正以避免类型转换异常。
内容的提问来源于stack exchange,提问作者sk001
相关产品推荐
相关产品推荐

