Mockito SpyBean新提取方法未返回存根值,仍执行真实方法?
问题描述
我使用SpringMVC + JUnit 5结合Mockito编写测试类,配置如下:
@RunWith(SpringRunner.class) @WebMvcTest(MyController.class) @TestPropertySource("classpath:application-unit-test.properties") public class MyTestClass { @Autowired MockMvc mockMvc; @SpyBean MyImpl myImpl; @MockBean MyBean aFewBeansLikeThis; }
控制器依赖作为Spy的myImpl,此前测试中通过doReturn语句配置存根后调用mockMvc.perform(post()),所有测试均正常通过。但我在myImpl中新提取了一个public方法并为其配置存根后,测试却始终执行该SpyBean的真实方法,而非返回存根值。我尝试了Mockito的两种存根语法:
doReturn(myObj).when(myImpl1).myMethod();
和
when(myImpl1.myMethod()).thenReturn(myObj);
且通过断点确认注入的mock和spy实例确实被使用,该类其他方法的存根均正常,重建项目也无法解决问题。想请教SpyBean中存根方法仍执行真实方法的可能原因。
可能的原因及解决方案
- 新方法是final修饰:Mockito基于动态代理实现Spy功能,无法拦截final方法——JVM会直接调用真实逻辑,跳过代理层。检查
myMethod()的修饰符,去掉final后重新测试。 - 方法被内部直接调用:如果
myMethod()是被MyImpl内部其他方法通过this关键字调用,会绕过Spy代理对象,直接执行真实对象的方法。比如:
解决方式:将public class MyImpl { public void businessMethod() { // this指向真实对象,而非Spy代理 this.myMethod(); } public void myMethod() { ... } }MyImpl自身作为依赖注入(自注入),通过依赖对象调用myMethod(),而非直接用this。 - 存根配置时机过晚:如果在
mockMvc.perform()之后才配置myMethod()的存根,此时方法已经被调用,存根不会生效。确保所有存根配置都在mockMvc.perform()执行之前完成。 - 方法是static修饰:Mockito默认不支持Spy静态方法,若
myMethod()是static修饰,存根会完全失效。若必须mock静态方法,需使用Mockito 3.4+提供的mockStatic()API,但建议优先重构代码避免静态方法依赖。 - 存根对象引用错误:检查存根代码中使用的对象是否是注入的
myImpl——比如你代码中写的是myImpl1,但实际注入的是myImpl,这种情况下存根配置的是未被使用的对象,自然不会生效。 - MyImpl类是final类:如果
MyImpl本身是final类,Mockito无法生成代理类实现Spy功能,所有方法都会执行真实逻辑。检查类的修饰符,去掉final后重试。
内容的提问来源于stack exchange,提问作者Victor Grazi
相关产品推荐
相关产品推荐

