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

Mockito4.5.1模拟带时区LocalDateTime.now及时区异常解决

问题原因

Mockito的mockStatic(LocalDateTime.class)会拦截该类所有静态方法的调用,包括JDK内部加载时区规则时间接依赖的LocalDateTime方法(如ZoneOffsetTransition中的相关逻辑),导致时区数据初始化异常,抛出ZoneRulesException。

解决方案

方案1:重构代码规避静态方法Mock(推荐)

把获取当前时间的逻辑抽象为可注入的接口,从根源上避免静态方法Mock带来的副作用:

  1. 定义时间提供接口:
public interface TimeProvider {
    LocalDateTime getCurrentLocalDateTime(ZoneId zoneId);
}
  1. 实现默认业务逻辑:
public class DefaultTimeProvider implements TimeProvider {
    @Override
    public LocalDateTime getCurrentLocalDateTime(ZoneId zoneId) {
        return LocalDateTime.now(zoneId);
    }
}
  1. 修改业务代码注入依赖:
public class MyLocalDateTimeUtil {
    private final TimeProvider timeProvider;

    public MyLocalDateTimeUtil(TimeProvider timeProvider) {
        this.timeProvider = timeProvider;
    }

    public LocalDateTime getCurrentLocalDateTime() {
        return timeProvider.getCurrentLocalDateTime(ZoneId.of("America/New_York"));
    }
}
  1. 单元测试Mock接口即可:
@Test
void testGetCurrentLocalDateTime() {
    TimeProvider mockProvider = mock(TimeProvider.class);
    LocalDateTime fixedTime = LocalDateTime.of(2024, Month.JANUARY, 1, 0, 0);
    when(mockProvider.getCurrentLocalDateTime(any(ZoneId.class))).thenReturn(fixedTime);

    MyLocalDateTimeUtil util = new MyLocalDateTimeUtil(mockProvider);
    LocalDateTime result = util.getCurrentLocalDateTime();

    assertEquals(fixedTime, result);
}

方案2:提前初始化时区规则

在Mock LocalDateTime静态方法之前,先初始化目标时区,让JDK提前加载好对应的时区规则,后续Mock不会再触发加载逻辑:

@Test
void testGetCurrentLocalDateTime() {
    // 提前加载纽约时区规则
    ZoneId nyZone = ZoneId.of("America/New_York");

    try (MockedStatic<LocalDateTime> mockedLocalDateTime = mockStatic(LocalDateTime.class)) {
        LocalDateTime fixedLocalDateTime = LocalDateTime.of(2024, Month.JANUARY, 1, 0, 0, 0);
        // 用精确匹配代替any,避免不必要的Mock范围
        mockedLocalDateTime.when(() -> LocalDateTime.now(eq(nyZone))).thenReturn(fixedLocalDateTime);

        LocalDateTime result = MyLocalDateTimeUtil.getCurrentLocalDateTime();
        assertEquals(fixedLocalDateTime, result);
    }
}

方案3:配置Mock放行必要静态方法

通过CALLS_REAL_METHODS让Mock默认调用真实方法,仅对目标方法做Mock:

@Test
void testGetCurrentLocalDateTime() {
    // 指定默认调用真实方法,仅Mock目标方法
    try (MockedStatic<LocalDateTime> mockedLocalDateTime = mockStatic(LocalDateTime.class, Mockito.CALLS_REAL_METHODS)) {
        LocalDateTime fixedLocalDateTime = LocalDateTime.of(2024, Month.JANUARY, 1, 0, 0, 0);
        mockedLocalDateTime.when(() -> LocalDateTime.now(any(ZoneId.class))).thenReturn(fixedLocalDateTime);

        LocalDateTime result = MyLocalDateTimeUtil.getCurrentLocalDateTime();
        assertEquals(fixedLocalDateTime, result);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 01:53:15