SpringBoot单元测试:无需PowerMockito模拟LocalDateTime报错解决
解决SpringBoot单元测试中模拟LocalDateTime的问题
你遇到的MissingMethodInvocationException本质原因是LocalDateTime是final类,且now()是静态方法,Mockito默认无法对这类非mock对象的final/静态方法进行存根(stub),直接在when()里调用LocalDateTime.now()不符合Mockito的使用规则。
下面是三种无需PowerMockito的可行方案:
方案1:封装时间获取逻辑(推荐,符合依赖倒置原则)
把获取当前时间的逻辑封装成可注入的接口,业务代码依赖这个接口而非直接调用LocalDateTime.now(),测试时只需mock这个接口即可。
步骤示例:
- 定义时间提供者接口
public interface TimeProvider { LocalDateTime getCurrentLocalDateTime(); }
- 实现生产环境的时间提供者
@Component public class DefaultTimeProvider implements TimeProvider { @Override public LocalDateTime getCurrentLocalDateTime() { return LocalDateTime.now(); } }
- 业务类注入该接口
@Service public class YourBusinessService { private final TimeProvider timeProvider; public YourBusinessService(TimeProvider timeProvider) { this.timeProvider = timeProvider; } public LocalDateTime doSomething() { // 用接口获取当前时间,而非直接调用LocalDateTime.now() LocalDateTime now = timeProvider.getCurrentLocalDateTime(); // 业务逻辑... return now; } }
- 单元测试mock TimeProvider
@ExtendWith(MockitoExtension.class) public class YourBusinessServiceTest { @Mock private TimeProvider timeProvider; @InjectMocks private YourBusinessService businessService; @Test public void testDoSomething() { // 模拟固定时间 LocalDateTime fixedTime = LocalDateTime.of(2024, 5, 20, 10, 30); when(timeProvider.getCurrentLocalDateTime()).thenReturn(fixedTime); LocalDateTime result = businessService.doSomething(); assertEquals(fixedTime, result); } }
方案2:使用Mockito Inline Mock Maker(支持mock final类/静态方法)
从Mockito 3.4.0版本开始,提供了inline mock maker,可以直接mock final类和静态方法,无需PowerMockito。
配置步骤:
- 在测试资源目录下创建文件:
src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker - 文件内容仅需一行:
mock-maker-inline
- 测试中mock LocalDateTime的静态方法(注意用try-with-resources自动关闭mock)
@ExtendWith(MockitoExtension.class) public class YourTest { @Test public void testWithMockedLocalDateTime() { LocalDateTime fixedTime = LocalDateTime.of(2024, 5, 20, 10, 30); // mock LocalDateTime的静态方法 try (MockedStatic<LocalDateTime> mockedLocalDateTime = Mockito.mockStatic(LocalDateTime.class)) { mockedLocalDateTime.when(LocalDateTime::now).thenReturn(fixedTime); // 执行测试逻辑,此时LocalDateTime.now()会返回fixedTime LocalDateTime result = LocalDateTime.now(); assertEquals(fixedTime, result); } } }
方案3:修改业务方法,传入时间参数
如果业务逻辑允许,可以将时间作为方法参数传入,测试时直接传入固定时间,完全避免依赖静态方法。
示例:
业务方法修改为:
public LocalDateTime doSomething(LocalDateTime currentTime) { // 直接使用传入的时间 // 业务逻辑... return currentTime; }
测试时直接传固定值:
@Test public void testDoSomething() { LocalDateTime fixedTime = LocalDateTime.of(2024, 5, 20, 10, 30); LocalDateTime result = businessService.doSomething(fixedTime); assertEquals(fixedTime, result); }
内容的提问来源于stack exchange,提问作者NNFdo
相关产品推荐
相关产品推荐

