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

Mockito使用mockStatic()时,如何模拟作用域内Mock对象的实例方法?

解决Mock Instant.now()后实例方法返回null的问题

问题原因

当使用Mockito的mockStatic(Instant.class)时,由于Instant是final类,Mockito会拦截该类的所有方法调用(包括实例方法),默认返回null。即使你让Instant.now()返回一个真实的Instant实例,后续调用该实例的minus()等方法时,也会被Mockito拦截并返回null。


最优解决方案:依赖Clock而非直接调用Instant.now()

这是更符合软件设计原则的做法,通过注入Clock隔离系统时钟依赖,避免直接静态调用Instant.now(),让测试更简单且无需mock静态方法。

1. 修改业务代码,注入Clock

@Component
class DeletionScheduler {
    private final Clock clock;
    private final ShareService shareService;
    private final Duration validityGracePeriod; // 假设为类中已定义的成员变量

    // 构造函数注入Clock与依赖服务
    public DeletionScheduler(Clock clock, ShareService shareService, Duration validityGracePeriod) {
        this.clock = clock;
        this.shareService = shareService;
        this.validityGracePeriod = validityGracePeriod;
    }

    public void findOutdatedEntries() {
        // 通过注入的Clock获取当前时间
        Instant limitDate = Instant.now(clock).minus(validityGracePeriod);
        List<Entry> entriesToDelete = shareService.findOutdatedEntries(limitDate);
        // 后续业务逻辑...
    }
}

2. 编写测试用例

通过创建固定时间的Clock模拟系统时钟,无需mock任何静态方法:

@Test
void findOutdatedEntries_whenClockSetToFuture_thenFindsExpiredEntries() {
    // 1. 定义测试用的未来时间
    String futureTimeStr = "2025-12-22T10:15:30Z";
    Instant futureInstant = Instant.parse(futureTimeStr);
    Clock fixedClock = Clock.fixed(futureInstant, ZoneId.of("UTC"));

    // 2. 构造过期条目:条目时间早于未来时间减去宽限期
    Duration validityGracePeriod = Duration.ofWeeks(2);
    Instant limitDate = futureInstant.minus(validityGracePeriod);
    Entry expiredEntry = new Entry(limitDate.minus(Duration.ofDays(1))); // 该条目已过期

    // 3. Mock ShareService
    ShareService mockShareService = mock(ShareService.class);
    when(mockShareService.findOutdatedEntries(eq(limitDate)))
            .thenReturn(List.of(expiredEntry));

    // 4. 初始化组件并执行方法
    DeletionScheduler scheduler = new DeletionScheduler(fixedClock, mockShareService, validityGracePeriod);
    scheduler.findOutdatedEntries();

    // 5. 验证逻辑正确性
    verify(mockShareService).findOutdatedEntries(limitDate);
    // 可添加更多断言,比如确认返回的条目是预期的过期条目
}

临时解决方案:Mock静态方法时保留实例方法的真实逻辑

如果无法修改业务代码,必须mockInstant.now(),可以开启Mockito对final类的mock支持,并配置让实例方法调用真实实现:

1. 开启final类mock支持

在src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker文件中添加:

mock-maker-inline

2. 修改测试用例

@Test
void givenInstantMock_whenNow_thenGetFixedInstant() {
    String instantExpected = "2014-12-22T10:15:30Z";
    Instant realInstant = Instant.parse(instantExpected);

    try (MockedStatic<Instant> mockedStatic = mockStatic(Instant.class)) {
        // 让Instant.now()返回真实的Instant实例
        mockedStatic.when(Instant::now).thenReturn(realInstant);
        // 配置所有Instant实例的方法调用真实逻辑
        mockedStatic.when(() -> any(Instant.class).minus(any())).thenCallRealMethod();

        Instant now = Instant.now();
        assertThat(now.toString()).isEqualTo(instantExpected);

        Instant earlier2Weeks = now.minus(Period.ofWeeks(2));
        assertNotNull(earlier2Weeks);
        assertThat(earlier2Weeks).isEqualTo(realInstant.minus(Period.ofWeeks(2)));
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 20:52:06