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

JDK 17下Mockito静态Mock失败 Instant.now()返回null如何解决?

问题根因
  • JDK 17 默认开启java.base核心模块强封装,java.time.Clock、java.time.Instant这类内置时间类都在该模块下,默认禁止第三方库通过字节码注入修改类行为。Mockito 实现静态方法Mock靠的是inline mock maker做字节码增强,拿不到对应模块的访问权限时,根本拦截不到Clock的静态方法调用,打桩完全不生效,没被处理的方法默认返回Mockito的默认值null。
  • 这个问题和Windows系统没关系,所有JDK 17及以上版本,只要没做模块权限配置,Mock JDK核心类的静态方法都会出同类问题。JDK 11没有强制开启强封装,所以同样的代码跑起来没问题。
解决方案

按改造成本从低到高,三个方案可选:

方案1:加JVM参数开放模块权限(改动最小,不用改代码)

不管是IDE里跑测试,还是用Maven Surefire、Gradle test跑自动化测试,给测试任务加两个JVM启动参数就行,给Mockito开操作java.time包的权限:

--add-opens java.base/java.time=ALL-UNNAMED
--add-opens java.base/java.lang=ALL-UNNAMED

加完原有代码不用动,JDK17下就能正常跑通。

方案2:升级Mockito版本

把Mockito升到4.8.0以上的版本,高版本对JDK17+的适配更好,但要注意:就算是最新版Mockito,Mock JDK核心类的时候还是要配上面的--add-opens参数,这是JDK层面的限制,库本身绕不过去。

方案3:重构代码去掉静态方法依赖(最推荐的长期方案)

业务代码里别直接写Instant.now()这种静态调用,改成依赖注入Clock实例,从根源上不用Mock静态方法,完全不会有JDK版本兼容的问题。
举个改造例子:
原来的业务代码硬调静态方法:

public class TimeService {
    public Instant getNow() {
        return Instant.now();
    }
}

改成注入Clock的写法:

public class TimeService {
    private final Clock clock;

    // 生产环境直接注入Clock.systemUTC(),测试的时候传Mock对象就行
    public TimeService(Clock clock) {
        this.clock = clock;
    }

    public Instant getNow() {
        return clock.instant();
    }
}

测试代码连mockStatic都不用写,逻辑简单还稳定:

@Test
void testGetNow() {
    Instant expected = Instant.parse("2022-03-10T10:15:30Z");
    Clock mockClock = mock(Clock.class);
    when(mockClock.instant()).thenReturn(expected);
    TimeService service = new TimeService(mockClock);
    assertEquals(expected, service.getNow());
}

补充:你原来测试里写的spy(Clock.class)本身没语法问题,权限配置对的情况下是可以正常工作的,不用改。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 21:57:22