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

Mockito模拟Instant计算Duration时遇空指针及反射访问错误问题

解决Mockito模拟Instant时Duration计算报错及反射权限问题

问题场景

使用Mockito 4.7.0静态模拟Instant.now()返回固定时间,调用Duration.between()时触发错误:Cannot read field "seconds" because "end" is null;尝试添加Mockito.CALLS_REAL_METHODS调用真实方法后,又出现反射访问权限错误:class org.mockito.internal.util.reflection.ReflectionMemberAccessor cannot access a member of class java.time.Instant (in module java.base) with modifiers "private static"。

问题原因

  1. 静态mock整个Instant类时,Mockito默认会mock所有未显式stub的方法,导致Duration.between()依赖的Instant内部方法返回默认值(如null),触发空指针错误。
  2. java.time.Instant属于JDK核心模块java.base,模块系统限制了反射对私有静态成员的访问,因此添加CALLS_REAL_METHODS会触发权限异常。

解决方案

方案1:避免静态mock,封装时间获取逻辑(推荐)

将获取当前时间的逻辑封装为可注入的依赖(如Clock),测试时使用固定时钟替代静态方法调用,彻底规避静态mock的问题。

修改业务代码:

import java.time.{Clock, Duration, Instant}

class ClassToTest(clock: Clock = Clock.systemUTC()) {
  def run(): Duration = {
    val now = Instant.now(clock)
    val now1 = Instant.now(clock)
    Duration.between(now1, now)
  }
}

测试代码:

import org.junit.Test
import java.time.{Clock, Instant, ZoneId}

@Test
def test_duration(): Unit = {
  val fixedInstant = Instant.parse("2022-08-31T08:55:48.200Z")
  val fixedClock = Clock.fixed(fixedInstant, ZoneId.of("UTC"))
  val testee = new ClassToTest(fixedClock)
  
  val result = testee.run()
  // 验证结果:两次获取的时间相同,Duration应为0
  assert(result.isZero)
}

方案2:正确配置静态mock(仅作兼容参考)

若必须使用静态mock,需确保仅stubInstant.now(),同时让其他方法调用真实实现,且规避JDK模块权限限制:

  1. 在测试启动的JVM参数中添加:
--add-opens java.base/java.time=ALL-UNNAMED
  1. 修改测试代码:
import org.junit.Test
import org.mockito.Mockito.{mockStatic, CALLS_REAL_METHODS}
import java.time.{Duration, Instant}

@Test
def test_duration(): Unit = {
  val testee = new ClassToTest()
  val timeMocked = Instant.parse("2022-08-31T08:55:48.200Z")
  val mock = mockStatic(classOf[Instant], CALLS_REAL_METHODS)
  try {
    mock.when(() => Instant.now()).thenReturn(timeMocked)
    val result = testee.run()
    assert(result.isZero)
  } finally {
    if (mock != null) mock.close()
  }
}

说明

方案1遵循依赖注入原则,代码更易测试和维护,是推荐的最佳实践;方案2需修改JVM参数,且静态mock可能带来其他潜在问题,仅在无法修改业务代码时使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 20:48:20