Boot4升级后MockBean和MockMvc怎么改

Boot4升级后MockBean和MockMvc怎么改

升到 Spring Boot 4 之后,单测经常是「编译红 + 运行红」一起撞:@MockBean 找不到符号;注解改完了,@SpringBootTest 里 @Autowired MockMvc 又缺 Bean。这两类坑在 Spring Boot 4.0 Migration Guide 里写得很直,却容易被当成「依赖没对齐」或「测试类写错了」。本文只讲测试注解迁移:换 @MockitoBean / @MockitoSpyBean、给 MockMvc 显式加 @AutoConfigureMockMvc、以及共享 mock 不能 再写在 @Configuration 字段上。生命周期注解(javax → jakarta)另文已写,这里只作内链旁证。

编译失败先拦住一批人;另一批人 PR 能绿编译,却在 CI 集成测才爆 MockMvc 装配失败。两边日志不一样,值班容易互相甩锅。升级 DoD 里把「旧注解是否清零」和「凡 @SpringBootTest 用 MockMvc 的类是否都加了 AutoConfigure」拆成两条,比事后在红海里翻栈便宜。

一、现象:编译期找不到,运行期 MockMvc 空

升级 Boot 4 后,测试侧最常见的两类报错:

  1. 编译期 :cannot find symbol: class MockBean / SpyBean。旧代码里大量 @MockBean private XxxService xxx; 直接红。
  2. 运行期 :测试类仍是 @SpringBootTest,注入 MockMvc 时 NoSuchBeanDefinitionException,或字段为 null------「以前能测 Controller,现在装配不上」。

根因不同,却常塞进同一个升级 PR:前者是 Boot 侧 移除 了 @MockBean / @SpyBean 支持;后者是 @SpringBootTest 不再自动提供任何 MockMvc 支持。拆开改,日志才不会互相掩盖。

现场排查时建议先看失败阶段:

  • 编译失败 → 优先搜 @MockBean / @SpyBean 是否还在源码里,而不是先怀疑 Mockito 版本或 IDE 缓存;
  • 编译过、测试启动失败 → 看异常是否点名 MockMvc(或旁证里的 WebClient / TestRestTemplate),再决定加哪条 @AutoConfigure*;
  • 只有部分测试红 → 往往是「字段级已改名、共享 Configuration 还没收」或「切片测试用了 MockMvc 却漏加注解」。

同系列升级里,若还在修「@PostConstruct 不跑」,那是 javax → jakarta 生命周期线,见 Boot4升级PostConstruct不跑了。本篇不抢那条主线。生产语义和测试装配最好拆两个 checklist,塞进一个巨型 PR 时失败日志会糊在一起。

旧写法 → 新写法先钉死:

二、注解替换:MockBean → MockitoBean(两对写全)

Migration Guide 写明:Spring Boot 的 @MockBean / @SpyBean 支持在本版 已移除 ,改用 Framework 侧的 @MockitoBean / @MockitoSpyBean。测试类字段一对一替换即可。

旧(Boot 侧,已移除) 新(Framework 测试支持)
@MockBean @MockitoBean
@SpyBean @MockitoSpyBean

最小对照(测试类字段直接换名即可):

java 复制代码
package com.example.demo;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.bean.override.mockito.MockitoBean;

@SpringBootTest
class GreetingControllerTest {

    @Autowired
    private GreetingController greetingController;

    @MockitoBean
    private GreetingService greetingService;

    @Test
    void usesMockedService() {
        given(greetingService.greet()).willReturn("ok");
        assertThat(greetingController.hello()).isEqualTo("ok");
    }
}

@SpyBean 同理改成 @MockitoSpyBean。包名以工程里 Framework 测试依赖实际解析到的为准。别只改一半:仓库里若还有 @SpyBean,编译会继续红。

全仓检索建议:

bash 复制代码
rg -n '@MockBean|@SpyBean' --glob '*Test*.java' --glob '*Tests.java'

也建议同时扫非 *Test* 命名的测试支持类(例如 *IT.java、*Tests.java、放在 src/test 下的 @TestConfiguration):

bash 复制代码
rg -n '@MockBean|@SpyBean' src/test

先在测试模块改完、跑通,再处理「共享 mock 写在 Configuration」那种结构性用法(见第四节)。两对注解都要写全:只改 @MockBean 留下 @SpyBean,下一轮 CI 仍会红。字段级替换多半是机械活;真正耗时的是以前靠 Configuration 字段共享 mock 的结构改造。

硬断言都来自 Boot 4.0 Migration Guide 的 @MockBean / @SpyBean Removal 与 MockMvc 相关小节。指南里没有的「某插件会自动改写注解」「运行时会把旧注解桥接成新注解」,一律不写。

三、MockMvc:必须加 @AutoConfigureMockMvc

Migration Guide「Using MockMVC and @SpringBootTest」写明:使用 @SpringBootTest 将不再提供任何 MockMvc 支持 。若要用 MockMvc,在测试类上加 @AutoConfigureMockMvc。

Boot 3.x 里很多人习惯「只写 @SpringBootTest 就能 @Autowired MockMvc」。Boot 4 把这条隐式装配拿掉了------现象是注入失败或 Bean 缺失;根因按官方说法,不是 Mockito 版本问题。

java 复制代码
package com.example.demo;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;

@SpringBootTest
@AutoConfigureMockMvc
class HelloMvcTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void helloReturnsOk() throws Exception {
        mockMvc.perform(get("/hello"))
                .andExpect(status().isOk())
                .andExpect(content().string("ok"));
    }
}

若项目用了 HtmlUnit 相关开关,Boot 3.5 常见写法是:

java 复制代码
@AutoConfigureMockMvc(webClientEnabled = false, webDriverEnabled = false)

Boot 4 迁到嵌套形态(Migration Guide 对照):

java 复制代码
@AutoConfigureMockMvc(htmlUnit = @HtmlUnit(webClient = false, webDriver = false))

没写到 HtmlUnit 的测试类,加 @AutoConfigureMockMvc 即可,不必展开这层属性。注解名写 @HtmlUnit,按 Migration Guide 改属性形态,别臆造别名。

常见误判还有两类:

  1. 「我用的是 @WebMvcTest,是不是也要加?」 ------切片测试本身通常已带 MockMvc 相关自动配置;本篇主线是 @SpringBootTest 不再自动给 MockMvc。升级时以你实际用的测试注解为准,对照指南对应节,不要把所有测试类无差别堆上同一条注解。
  2. 「加了 @AutoConfigureMockMvc 还缺 Bean」 ------先确认测试是否真的落在 Boot 4 的测试自动配置包路径上,以及是否被自定义 @Import / 排除自动配置误伤;本文不展开模块化 starter 主线,只提醒:缺 Bean 时优先核对「有没有按指南显式打开」,再查排除列表。

MockMvc 与注解替换可以画成一条升级时序:

四、共享 mock:别再写在 @Configuration 字段上

这条最容易漏。Migration Guide 明确:新注解 允许 作为测试类 字段使用,但 不能 写在 @Configuration 类的字段上。

旧习惯里,有人会建一个 @TestConfiguration / @Configuration,字段上挂一堆 @MockBean,多个测试类 @Import 共用。Boot 4 这条路不通了。

官方给出的共享方式主要有两种:

1. 类级 types=

java 复制代码
package com.example.demo;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.bean.override.mockito.MockitoBean;

@SpringBootTest
@MockitoBean(types = {OrderService.class, UserService.class})
class OrderFlowTest {

    @Test
    void placeholder() {
        // OrderService / UserService 已按类型注册为 mock
    }
}

2. 自定义组合注解(meta-annotation)

java 复制代码
package com.example.demo;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.test.context.bean.override.mockito.MockitoBean;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@MockitoBean(types = {OrderService.class, UserService.class})
public @interface SharedMocks {
}

测试类上写 @SharedMocks 即可复用同一组 mock 声明。字段级 @MockitoBean 仍留给「这个测试类自己要 mock 的那个依赖」。

若 IDE 还允许你在 @Configuration 字段上写 @MockitoBean,也别当成官方支持------Migration Guide 的约束是语义层的,不是「编译器暂时没拦」。@TestConfiguration 同样别当避风港:共享声明挪到测试类型别上的 types= 或组合注解。

迁移共享 mock 时的务实顺序:

  1. 列出被多个测试 @Import 的旧 Configuration / TestConfiguration;
  2. 把其中的 @MockBean / @SpyBean 字段改写成一份 @MockitoBean(types={...}) 或一个 @SharedMocks;
  3. 各测试类改为标注该组合注解(或直接类级 types=),删除字段挂载;
  4. 跑受影响的测试子集,确认 mock 协作对象仍按类型注入。

共享清单集中在一处,Code Review 能直接看见「到底 mock 了哪些类型」,而不是散落在配置类字段里。

五、旁证一句:WebClient / TestRestTemplate 也不再自动给

同一篇 Migration Guide 还有一节:@SpringBootTest 不再 自动提供 WebClient 或 TestRestTemplate beans。需要 TestRestTemplate 时加 @AutoConfigureTestRestTemplate(并按指南补依赖);也可考虑 RestTestClient + @AutoConfigureRestTestClient。这里只作旁证------升测装配时若同时缺 HTTP 客户端 Bean,先对照这一节,别和 MockMvc 混成一个「全坏了」的故事。官方该节主语写的是 WebClient(以及 TestRestTemplate)。

Jackson 3、starter 模块化等是 Boot 4 的其他大节,今日不写。升级窗口里如果同时在改序列化与测试注解,建议先保证测试能编译、能装配到要测的端点,再处理序列化差异------否则「接口断言失败」分不清是 mock 没挂上,还是 JSON 形状变了。

六、常见误判(升级评审可当 checklist)

「编译报错是因为没引 mockito-core」 ------先搜源码里是否还在用已移除的 @MockBean / @SpyBean。注解名换了,不是单纯缺依赖。

「以前 @SpringBootTest 就能注入 MockMvc,肯定是我项目配坏了」 ------Migration Guide 明文:仅用 @SpringBootTest 不再提供任何 MockMvc 支持 。先加 @AutoConfigureMockMvc,再查自定义排除。

「新注解还能写在 @Configuration 字段上,跟旧的一样」 ------官方写的是:允许测试类字段,不能 写在 @Configuration 类字段。共享请用 types= 或组合注解。

「只改 MockBean 就行,SpyBean 很少用可以以后再说」 ------两对都已移除。漏改的 @SpyBean 会在下一处编译失败里再爆一次。

「HtmlUnit 还按 webClientEnabled 那套属性写」 ------Boot 4 对照写法是 htmlUnit = @HtmlUnit(webClient=..., webDriver=...)。没碰到 HtmlUnit 就不用展开;碰到了按指南改形态。

「缺 WebClient Bean 和 MockMvc 是同一类自动配置」 ------同属「@SpringBootTest 不再自动给」家族,但对应不同的 @AutoConfigure*。旁证一句即可,按需加,勿混成一个万能注解。

七、迁移 checklist 与 FAQ

按固定顺序改,别「先全仓替换字符串再看 CI」:

  1. 搜 @MockBean / @SpyBean(含测试与 @TestConfiguration、IT 命名)。
  2. 测试类字段 改成 @MockitoBean / @MockitoSpyBean;跑通单测模块编译。
  3. 凡用 MockMvc 的 @SpringBootTest ,补 @AutoConfigureMockMvc;HtmlUnit 开关改 htmlUnit = @HtmlUnit(...)。
  4. 拆掉 Configuration 字段上的 mock ;改成 types= 或 @SharedMocks 组合注解。
  5. 若还注入 WebClient / TestRestTemplate 失败,对照 Migration Guide 对应节加 AutoConfigure(旁证,勿拖长本 PR 主线)。
  6. 回归:至少一条 Controller MockMvc 用例 + 一条带 mock 协作对象的切片/集成测试。
  7. Code Review 抽查:不应再出现 Configuration 字段级 @MockitoBean;标题/注释里若仍写「MockBean」要改成新名,避免后人抄旧示例。

灰度或分模块升级时,可以先让「纯单元 + Mockito 手写 mock」保持绿,再集中处理 @SpringBootTest 集成测------后者才是 MockMvc / AutoConfigure 的重灾区。集成测全红时,按「编译符号 → MockMvc Bean → 共享 Configuration」三层过滤,比从业务断言倒推快。

FAQ:

  • 为什么找不到 @MockBean?

    Boot 4 已移除 Boot 侧 @MockBean / @SpyBean 支持,改用 @MockitoBean / @MockitoSpyBean。不是依赖漏引那么简单------注解本身换了。

  • 为什么 @SpringBootTest 里没有 MockMvc?

    官方说明:仅用 @SpringBootTest 不再提供任何 MockMvc 支持 。测试类加 @AutoConfigureMockMvc。

  • @MockitoBean 能否写在 @Configuration 里?

    不能 写在 @Configuration 字段上。允许用在测试类字段;共享用 @MockitoBean(types=...) 或自定义组合注解。

场景 建议
测试类单个依赖 mock 字段 @MockitoBean / @MockitoSpyBean
多测试共享同一组 mock 类级 types= 或 @SharedMocks
Controller 集成测 MockMvc @SpringBootTest + @AutoConfigureMockMvc
关闭 HtmlUnit WebClient/Driver htmlUnit = @HtmlUnit(webClient=false, webDriver=false)
Configuration 字段挂 mock 拆掉,迁到上面两种共享方式

八、小结

Boot 4 测试迁移记三连击就够:换注解名 (MockBean/SpyBean → MockitoBean/MockitoSpyBean)、显式开 MockMvc (@AutoConfigureMockMvc)、共享 mock 离开 Configuration 字段 (types= 或组合注解)。行为以 Spring Boot 4.0 Migration Guide 为准,不编「某版本自动改写注解」。生命周期 PostConstruct 静默失效见 167076265;本篇只收测试装配这条线。

三条进升级 DoD:全仓 rg 清零旧注解;凡 @SpringBootTest + MockMvc 的类扫一遍 @AutoConfigureMockMvc;共享 mock 只认 types= / 组合注解,不认 Configuration 字段。比事后在 CI 里逐个点开失败用例省事。

落地顺序:搜旧注解 → 字段改名 → 补 AutoConfigureMockMvc → 拆 Configuration 共享 mock → 再处理 WebClient/TestRestTemplate 旁证。 顺序反了,容易在「缺 Bean」的噪音里漏掉「注解已经不存在」的编译根因。

相关推荐
LXMXHJ2 年前
项目测试之MockMvc
mockmvc
深色風信子2 年前
SpringBoot MockMvc
spring boot·mockmvc·springboot 打桩
Python南帝3 年前
Spring Boot单元测试
spring boot·单元测试·spring boot单元测试·mockmvc·spring boot 单元·controller层单元测试·postman基本用法