17370845950

Spring Boot 单元测试中 MockMvc 为空的解决方案

本文详解 spring boot 中 `@webmvctest` 下 `mockmvc` 注入失败(为 null)的根本原因,包括注解冲突、版本不兼容及配置冗余问题,并提供规范、可运行的 web 层测试最佳实践。

在 Spring Boot 测试中,MockMvc 是验证控制器(Controller)行为的核心工具。但如你所遇——MockMvc 字段始终为 null,即使添加了 @Autowired 和各类测试注解(如 @SpringBootTest、@WebMvcTest、@AutoConfigureMockMvc),往往源于注解混用冲突依赖版本错配两大关键问题。

✅ 正确做法:单一职责 + 精准注解

@WebMvcTest 本身已自动启用 MockMvc 的自动配置,无需额外添加 @AutoConfigureMockMvc(它在此场景下冗余且可能干扰上下文加载),更严禁与 @SpringBootTest 同时使用——二者目标冲突:

  • @WebMvcTest:仅加载 Web 层(Controller + 配置),轻量、快速,适合 MVC 接口测试;
  • @SpringBootTest:加载完整应用上下文,重量级,适用于集成测试,会覆盖 @WebMvcTest 的隔离性,导致 MockMvc 注入失效。

✅ 正确测试类应精简如下:

@WebMvcTest(BookController.class) // 仅扫描 BookController 及其 Web 相关 Bean
class BookControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean // 自动 mock 依赖的 Service,避免真实调用
    private BookService bookService;

    @Test
    void testListBooks() throws Exception {
        // 模拟 service 返回值
        when(bookService.listBooks()).thenReturn(List.of(new Book("Spring Boot in Action")));

        mockMvc.perform(get("/api/books/list"))
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON));
    }
}

⚠️ 关键修复点说明

  1. 移除冲突注解
    删除 @SpringBootTest 和 @AutoConfigureMockMvc —— @WebMvcTest 已隐式包含所需配置。

  2. 补充 @MockBean
    Controller 通常依赖 Service,必须用 @MockBean 替代真实 Bean,否则 @WebMvcTest 会因找不到 BookService Bean 而启动失败或注入异常。

  3. 修正 Maven 依赖版本一致性
    你的 pom.xml 存在严重版本冲突:

    • 父 POM 为 Spring Boot 2.1.0.RELEASE,但 spring-boot-starter-web 显式指定为 3.0.0(属 Spring Boot 3.x,基于 Jakarta EE 9+,与 Spring Boot 2.x 不兼容);
    • JUnit 5 版本(5.2.0)过旧,且 mockito-junit-jupiter 4.6.1 与 Spring Boot 2.1 不匹配。

    ✅ 推荐统一使用 Spring Boot 2.7.x(LTS)或 3.2+(最新 LTS),并删除所有显式 ,交由父 POM 管理:

    
    
        org.springframework.boot
        spring-boot-starter-web
        
    

    Spring Boot 2.7.x 默认使用 JUnit Jupiter 5.8+ 和 Mockito 4.11+,完全兼容 @ExtendWith(MockitoExtension.class)(但 @WebMvcTest 已内置 Mockito 支持,@ExtendWith 亦可省略)。

  4. Controller 方法可见性修正
    你代码中 listBooks() 声明为 private,但 Spring MVC 要求 Handler 方法必须是 public:

    @GetMapping("/list")
    public ResponseEntity listBooks() { // ✅ 改为 public
        // ...
    }
    
    

    ? 总结:三步排错清单

    问题类型 错误表现 解决方案
    注解冲突 MockMvc 为 null,测试启动慢或报 NoSuchBeanDefinitionException ✅ 仅保留 @WebMvcTest(YourController.class),删除 @SpringBootTest、@AutoConfigureMockMvc
    依赖未 Mock MockMvc 不为 null 但请求抛 NullPointerException(service 未初始化) ✅ 添加 @MockBean YourService service 并设置 when(...).thenReturn(...)
    版本不一致 编译通过但运行时报 ClassNotFoundException 或 IncompatibleClassChangeError ✅ 删除所有显式 ,确保 spring-boot-starter-parent 与子模块版本严格对齐

    遵循以上规范,MockMvc 将被正确注入,测试可稳定执行——这是 Spring Boot Web 层单元测试的基石实践。