Mockito模拟ZonedDateTime时withZoneSameInstant返回null问题
在Java测试场景中,我需要用Mockito模拟ZonedDateTime.now()方法,同时通过MapStruct编写转换器实现ZonedDateTime到OffsetDateTime的转换。但测试时发现:调用zonedDateTime.withZoneSameInstant(ZoneOffset.UTC)始终返回null——尽管zonedDateTime本身是非空的。
尝试过直接调用zonedDateTime.toOffsetDateTime()能正常运行,但这种转换方式不符合规范,不是预期的实现方式。最终定位到是Mock配置的问题,通过在mockStatic时传入CALLS_REAL_METHODS参数解决了问题。
相关代码示例
MapStruct转换器
@Mapper(componentModel = "spring") public interface DateTimeConverter { default OffsetDateTime zonedToOffset(ZonedDateTime zonedDateTime) { if (zonedDateTime == null) { return null; } return zonedDateTime.withZoneSameInstant(ZoneOffset.UTC).toOffsetDateTime(); } }
最初的Mock工具方法(存在问题)
public static void mockZonedDateTimeNow(ZonedDateTime fixedDateTime) { try (MockedStatic<ZonedDateTime> mockedZonedDateTime = Mockito.mockStatic(ZonedDateTime.class)) { mockedZonedDateTime.when(ZonedDateTime::now).thenReturn(fixedDateTime); } }
测试方法
@Test void testDateTimeConversion() { ZonedDateTime fixedTime = ZonedDateTime.of(2024, 5, 20, 10, 0, 0, 0, ZoneId.of("Asia/Shanghai")); mockZonedDateTimeNow(fixedTime); DateTimeConverter converter = Mappers.getMapper(DateTimeConverter.class); OffsetDateTime result = converter.zonedToOffset(fixedTime); // 最初此处result为null,因为withZoneSameInstant返回null }
问题根源
Mockito默认对静态类做Mock时,会把该类所有方法都替换为Mock实现,包括ZonedDateTime的实例方法withZoneSameInstant。由于我们只显式Mock了now()静态方法,未对withZoneSameInstant做任何配置,调用该方法时就会返回默认的null值。
解决办法
在调用Mockito.mockStatic()时传入Mockito.CALLS_REAL_METHODS参数,指定未被显式Mock的方法执行真实逻辑。修改后的Mock工具方法如下:
public static void mockZonedDateTimeNow(ZonedDateTime fixedDateTime) { try (MockedStatic<ZonedDateTime> mockedZonedDateTime = Mockito.mockStatic(ZonedDateTime.class, Mockito.CALLS_REAL_METHODS)) { mockedZonedDateTime.when(ZonedDateTime::now).thenReturn(fixedDateTime); } }
这样配置后,只有ZonedDateTime.now()会被Mock逻辑覆盖,其他实例方法(如withZoneSameInstant)会执行真实代码,就能正常返回转换后的ZonedDateTime对象,最终得到正确的OffsetDateTime结果。
内容的提问来源于stack exchange,提问作者Burane

