You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用JUnit、Mockito或PowerMockito测试Java ExecutorService关闭逻辑?

覆盖ExecutorService关闭逻辑全分支的JUnit测试方案

你已经实现了基于ContextClosedEvent的ExecutorService关闭逻辑,但直接调用onApplicationEvent方法后,代码覆盖率仅覆盖首行。要覆盖awaitTermination和shutdownNow相关的所有分支,核心思路是用Mockito模拟ExecutorService的行为,控制各个方法的返回值或异常,从而触发不同分支逻辑。

测试依赖准备

确保测试类引入JUnit 5和Mockito依赖(如mockito-core、mockito-junit-jupiter),直接在测试类中使用Mockito注解即可。

具体测试用例

1. 场景:线程池正常终止(awaitTermination返回true)

此场景走最外层正常逻辑,不会触发shutdownNow。

测试代码:

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.ContextClosedEvent;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class YourEventListenerTest {

    @Mock
    private ExecutorService execService;

    @InjectMocks
    private YourEventListener eventListener; // 替换为你的事件监听类

    @Test
    void onApplicationEvent_正常终止() throws InterruptedException {
        // 模拟awaitTermination在60秒内返回true
        when(execService.awaitTermination(60, TimeUnit.SECONDS)).thenReturn(true);

        // 触发事件处理方法
        eventListener.onApplicationEvent(mock(ContextClosedEvent.class));

        // 验证方法调用次数
        verify(execService, times(1)).shutdown();
        verify(execService, times(1)).awaitTermination(60, TimeUnit.SECONDS);
        verify(execService, never()).shutdownNow(); // shutdownNow不会被调用

        // 验证日志输出
        Logger log = LoggerFactory.getLogger(YourEventListener.class);
        verify(log, times(1)).info(contains("ExecutorService shutdown hook called!"));
    }
}

2. 场景:首次等待超时,二次等待成功终止

此场景会触发shutdownNow,但第二次awaitTermination返回true,不会输出错误日志。

测试代码:

@Test
void onApplicationEvent_首次等待超时_二次成功终止() throws InterruptedException {
    // 首次awaitTermination返回false,第二次返回true
    when(execService.awaitTermination(60, TimeUnit.SECONDS))
            .thenReturn(false)
            .thenReturn(true);

    eventListener.onApplicationEvent(mock(ContextClosedEvent.class));

    verify(execService, times(1)).shutdown();
    verify(execService, times(2)).awaitTermination(60, TimeUnit.SECONDS);
    verify(execService, times(1)).shutdownNow();

    Logger log = LoggerFactory.getLogger(YourEventListener.class);
    verify(log, times(1)).info(contains("ExecutorService shutdown hook called!"));
    verify(log, never()).error(contains("Pool did not terminate"));
}

3. 场景:两次等待都超时,输出错误日志

此场景会触发shutdownNow,且第二次awaitTermination仍返回false,会输出错误日志。

测试代码:

@Test
void onApplicationEvent_两次等待都超时() throws InterruptedException {
    // 两次awaitTermination都返回false
    when(execService.awaitTermination(60, TimeUnit.SECONDS)).thenReturn(false);

    eventListener.onApplicationEvent(mock(ContextClosedEvent.class));

    verify(execService, times(1)).shutdown();
    verify(execService, times(2)).awaitTermination(60, TimeUnit.SECONDS);
    verify(execService, times(1)).shutdownNow();

    Logger log = LoggerFactory.getLogger(YourEventListener.class);
    verify(log, times(1)).info(contains("ExecutorService shutdown hook called!"));
    verify(log, times(1)).error(contains("Pool did not terminate"));
}

4. 场景:awaitTermination抛出InterruptedException

此场景会进入catch块,触发shutdownNow并中断当前线程。

测试代码:

@Test
void onApplicationEvent_等待被中断() throws InterruptedException {
    // 模拟awaitTermination抛出InterruptedException
    when(execService.awaitTermination(60, TimeUnit.SECONDS)).thenThrow(InterruptedException.class);

    eventListener.onApplicationEvent(mock(ContextClosedEvent.class));

    verify(execService, times(1)).shutdown();
    verify(execService, times(1)).awaitTermination(60, TimeUnit.SECONDS);
    verify(execService, times(1)).shutdownNow();

    // 验证当前线程被中断
    assert Thread.currentThread().isInterrupted();
}

注意事项

  • 将YourEventListener替换为你实际的事件监听类名称;
  • 若要精准断言日志,可使用slf4j-test工具捕获日志输出;
  • 所有测试用例无需手动处理InterruptedException,Mockito会模拟异常抛出逻辑。

内容的提问来源于stack exchange,提问作者vh1ne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 08:45:38