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
相关产品推荐
相关产品推荐

