是否存在类似Clock的简便方案可Mock System.nanoTime()?
如何简便Mock System.nanoTime()调用
JDK没有提供和Clock对等的、专门封装System.nanoTime()的官方工具类,但有两种和Mock System.currentTimeMillis()体验一致的简便方案,不需要做复杂改造:
方案1:自定义轻量时间源抽象(无依赖,最推荐)
完全照着Clock的设计思路写个极简接口就行,改造成本几乎为零:
public interface NanoClock { long nanoTime(); // 生产环境直接用这个默认实例,走真实的System.nanoTime()调用 NanoClock SYSTEM = () -> System.nanoTime(); }
后续业务编码时,不要直接硬编码调用System.nanoTime(),统一通过注入的NanoClock实例调用nanoTime()方法:
- 生产环境直接注入
NanoClock.SYSTEM,和原生调用的性能没有任何差异 - 单元测试时传入自定义Mock实现即可,不管是要返回固定值、还是按调用顺序返回递增值都可以,逻辑完全可控
方案2:测试框架静态Mock(适合存量老代码零改造)
如果项目里已经有大量直接调用System.nanoTime()的存量代码,不想逐处改造成注入形式,可以直接用测试框架的静态方法拦截能力实现Mock,不需要动业务代码。
Mockito 3.4以上版本原生支持静态方法Mock,写法非常简洁:
try (MockedStatic<System> systemMock = mockStatic(System.class)) { // 按调用顺序设定nanoTime的返回值,也可以固定返回单个值 systemMock.when(System::nanoTime).thenReturn(1000L, 2500L, 4000L); // 这个代码块内执行的所有业务逻辑,调用System.nanoTime()都会返回你预设的值 }
如果项目里已经引入了JMockit、PowerMock这类支持字节码增强的测试框架,也可以用对应框架提供的静态Mock能力拦截System.nanoTime()调用,使用这类方案注意确认框架和当前JDK版本的兼容性即可。
注意:
System.nanoTime()的返回值没有绝对时间语义,仅能用来计算耗时差,不能转换为日期类时间戳,Mock时也要遵守这个语义,不要混入currentTimeMillis的时间值,避免业务逻辑计算错误。
内容的提问来源于stack exchange,提问作者Brandon E Taylor
相关产品推荐
相关产品推荐

