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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 21:12:50