JUnit5 通过 TestExecutionListener 可实现测试方法毫秒级耗时统计,需在 executionStarted 和 executionFinished 中记录并计算 System.nanoTime() 差值;也可用 @BeforeEach/@AfterEach 配合 ConcurrentHashMap 简化实现。

JUnit5 本身不直接提供“每个测试方法耗时”的内置统计功能,但可以通过 TestExecutionListener(配合 @RegisterExtension 或 JVM 启动参数)在生命周期回调中精确记录开始/结束时间,从而实现毫秒级耗时统计。
使用 TestExecutionListener 监听测试执行阶段
TestExecutionListener 是 JUnit Platform 提供的扩展机制,可在测试容器、测试实例、测试方法等不同生命周期节点插入自定义逻辑。要统计单个测试方法耗时,关键是在 executionStarted 和 executionFinished 中捕获时间戳。
- 需实现
org.junit.platform.launcher.TestExecutionListener - 监听
TestIdentifier类型为CONTAINER或TEST,只对TEST类型(即实际测试方法)做计时 - 用
ThreadLocal<map long>></map>或并发安全的ConcurrentHashMap存储开始时间,避免多线程干扰
手动注册监听器并输出耗时
监听器不能通过注解自动生效,需在启动时注册。最常用方式是通过 LauncherDiscoveryRequest 构建 Launcher 并添加 listener;若用 Maven Surefire,默认不支持运行时注册,推荐改用 ConsoleLauncher 或 IDE 运行调试模式验证逻辑。
- 示例:在
main()中调用LauncherFactory.create().registerTestExecutionListener(new TimingListener()) - 在
executionStarted()中:对testIdentifier.isTest()的节点,存入System.nanoTime() - 在
executionFinished()中:取出对应开始时间,计算差值并转为毫秒((end - start) / 1_000_000.0),打印或写入日志
更轻量替代方案:@BeforeEach + @AfterEach 配合静态计时器
如果只需统计本类内测试方法耗时,且不依赖平台级扩展,可用 JUnit5 原生扩展点:TestInstancePostProcessor + InvocationInterceptor,或更简单地——在测试类中用 @BeforeEach/@AfterEach 手动打点:
立即学习“Java免费学习笔记(深入)”;
- 声明
private static final Map<string long> startTimes = new ConcurrentHashMap();</string> @BeforeEach void recordStart(TestMethod testMethod) { startTimes.put(testMethod.getDisplayName(), System.nanoTime()); }@AfterEach void logDuration(TestMethod testMethod) { long duration = (System.nanoTime() - startTimes.remove(testMethod.getDisplayName())) / 1_000_000.0; System.out.printf("[%s] %.2f ms%n", testMethod.getDisplayName(), duration); }- 注意:需引入
org.junit.jupiter.api.extension.TestMethod(JUnit 5.8+),旧版本可用ExtensionContext获取方法名
集成到报告中(可选增强)
若需结构化输出(如 JSON 或 CSV),可将耗时数据收集到全局 ConcurrentLinkedQueue<TimingRecord>,并在 afterAll(ExtensionContext) 中统一导出。TimingRecord 可包含:测试名、类名、耗时、状态(成功/失败)、异常信息(如有)。
- 避免在监听器中做 I/O 操作(如写文件),防止影响测试性能或引发并发问题
- 生产环境慎用,建议仅用于本地调试或 CI 中的专项性能分析
- 注意 JVM 预热和 GC 干扰,单次耗时仅供参考;多次运行取中位数更可靠


















