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

SpringBootTest中Mock后Autowired服务传Null等集成测试问题

Spring Boot API集成测试问题解决方案

我来逐个帮你解决这些问题,顺便梳理下正确的集成测试写法:


问题1:执行代码时提示“cannot call real method on abstract methods”

这个错误的原因很直白:你用@MockBean Mock了TestService接口(它是抽象的,没有具体实现),然后调用thenCallRealMethod()——抽象方法根本没有真实实现可以调用!而且你完全没必要Mock TestController本身,集成测试的核心就是让Spring加载真实的控制器实例,通过MockMvc去调用它的接口,而非Mock控制器。

直接删掉测试类里的@MockBean TestController testController;,同时移除针对控制器的Mockito.when(...)调用,这部分完全是多余的。


问题2:当Mock TestServiceImpl时,控制器中的TestService出现NullPointerException

空指针的根源在于:你的控制器依赖的是TestService接口,但你Mock的是TestServiceImpl实现类——Spring容器里会同时存在真实的TestService接口实现和你Mock的实现类,导致控制器无法正确注入Mock实例。

正确的做法是Mock接口而非实现类:使用@MockBean TestService testService;,这样Spring会自动把这个Mock实例注入到控制器中,空指针问题就解决了。


问题3:使用MongoDB时如何Mock仓库层?尝试Mock MongoTemplate时提示“MongoConvertor must not be null”

别直接Mock MongoTemplate,这玩意儿内部依赖复杂,Mock起来非常麻烦。Spring Data的Repository本身就是接口,直接Mock你的TestRepository接口才是最简洁的方案:

用@MockBean TestRepository repo;声明Mock实例,然后针对Repository的方法做Stub即可,示例:

Mockito.when(repo.save(Mockito.any(StringEntity.class))).thenReturn(new StringEntity("dude"));

这种方式既避免了操作真实数据库,又不用管MongoTemplate的内部依赖问题。


问题4:当前测试用例写法是否正确?不使用thenCallRealMethod()能否获得代码覆盖率?

当前的写法完全不正确,核心错误包括:

  • Mock了不需要Mock的控制器
  • 对抽象接口调用thenCallRealMethod()
  • 没有正确Stub依赖的底层方法

关于代码覆盖率:如果想要覆盖控制器和Service的真实代码,你需要让这些真实代码运行起来,而非只Mock它们返回固定值。正确的姿势是:

  1. 让Spring加载真实的TestController和TestServiceImpl实例
  2. Mock底层的TestRepository(避免真实DB操作)
  3. 通过MockMvc调用接口,让控制器调用真实的Service方法,Service再调用Mock的Repository方法

这样既完成了集成测试(覆盖控制器→Service的完整流程),又能获得控制器和Service的代码覆盖率,完全不需要用thenCallRealMethod()。


修正后的完整测试用例

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class MockitoTestingApplicationTests {

    @Autowired
    private MockMvc mvc;

    // Mock底层仓库,避免真实数据库操作
    @MockBean
    private TestRepository repo;

    @Test
    public void welcomeMessage_ShouldReturnOkAndCorrectContent() throws Exception {
        // Stub仓库的save方法,返回任意符合要求的实例即可
        Mockito.when(repo.save(Mockito.any(StringEntity.class))).thenReturn(new StringEntity("dude"));

        // 调用接口并验证状态和返回内容
        mvc.perform(get("/api/v1/welcome").param("name", "dude"))
                .andExpect(status().isOk())
                .andExpect(content().string("Hello dude"));
    }
}

额外提示:如果你的测试只需要覆盖控制器层(不需要加载整个Spring上下文),可以用@WebMvcTest(TestController.class)代替@SpringBootTest,测试启动会更快——它只会加载控制器相关的Bean,再用@MockBean注入TestService即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:22:20