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

构造器传参类使用@SpyBean时Mockito存根报错如何解决

问题根因
  • @SpyBean注解的作用是让Spring容器在初始化上下文时,生成被Mockito增强的Spy代理对象放入容器,你在@BeforeEach方法中手动new了一个普通的ComicService实例赋值给该字段,直接覆盖了原有的Spy代理。此时字段指向的是没有Mockito增强逻辑的普通Java对象,既无法支持打桩操作,参数匹配器执行后也无法关联到打桩流程,直接触发你看到的参数匹配器使用报错。
  • Spy对象打桩存在语法限制:使用Mockito.when(调用方法).thenReturn()语法打桩Spy方法时,会先真实执行一次原方法,getHash方法里有加密逻辑、依赖类成员变量,直接执行很容易触发意外异常,打桩Spy方法必须使用doReturn系列语法。
修复步骤
  1. 删除setUp方法中手动实例化ComicService的代码,通过测试配置给构造器注入所需的密钥参数,让Spring自动完成@SpyBean实例的创建。
    最简单的方式是在测试类上添加@TestPropertySource注解注入配置:

    @ExtendWith(SpringExtension.class)
    @ActiveProfiles("test")
    // 直接注入构造器需要的配置值
    @TestPropertySource(properties = {
            "marvel.public_key=ae78641e8976ffdf3fd4b71254a3b9bf",
            "marvel.private_key=eb9fd0d8r8745cd0d554fb2c0e7896dab3bb745"
    })
    public class ComicServiceTest {
        // 其余字段保持不变
        @SpyBean
        ComicService comicService;
        
        @MockBean
        MarvelClient marvelClient;
        
        // 删掉setUp里手动new对象的逻辑,不需要额外初始化
    }
    

    你也可以把这两个配置写到application-test.yml/application-test.properties测试配置文件里,效果一致。

  2. 替换getHash方法的打桩语法,改用Spy对象专用的doReturn().when()写法,避免真实执行原方法:
    把原来报错的打桩代码:

    Mockito.when(comicService.getHash(Mockito.anyString())).thenReturn("c6fc42667498ea8081a22f4570b42d03");
    

    替换为:

    Mockito.doReturn("c6fc42667498ea8081a22f4570b42d03")
            .when(comicService)
            .getHash(Mockito.anyString());
    
修复后完整测试类示例
@ExtendWith(SpringExtension.class)
@ActiveProfiles("test")
@TestPropertySource(properties = {
        "marvel.public_key=ae78641e8976ffdf3fd4b71254a3b9bf",
        "marvel.private_key=eb9fd0d8r8745cd0d554fb2c0e7896dab3bb745"
})
public class ComicServiceTest {
    
    @SpyBean
    ComicService comicService;
    
    @MockBean
    MarvelClient marvelClient;

    @Test
    public void getComicByApiTest() {
        // 构造测试返回数据
        MarvelAPIModelDTO foundMarvelAPIModelDTO = createMarvelAPIModelDTO();
        
        // 打桩同类方法getHash
        Mockito.doReturn("c6fc42667498ea8081a22f4570b42d03")
                .when(comicService)
                .getHash(Mockito.anyString()); 

        // 打桩外部依赖mock的方法
        Mockito.when(marvelClient.getComic(Mockito.anyInt(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString()))
                .thenReturn(foundMarvelAPIModelDTO);
        
        // 执行被测方法
        MarvelAPIModelDTO marvelAPIModelDTO = comicService.getComicByApi(1);
        
        // 结果断言
        Assertions.assertThat(marvelAPIModelDTO.getData().getResults().get(0).getId()).isEqualTo(1);        
    }

}

补充:如果是纯单元测试不需要加载Spring上下文,可以不用@SpyBean/@MockBean,直接用@Spy、@Mock、@InjectMocks组合,手动构造实例后用Mockito.spy()包装即可,Spring Boot集成测试场景下优先用上述@SpyBean+配置注入的方案,保证对象被Spring容器正确管理。

内容的提问来源于stack exchange,提问作者Gustavo Cruz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 01:12:17