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 后,测试侧最常见的两类报错:
- 编译期 :
cannot find symbol: class MockBean/SpyBean。旧代码里大量@MockBean private XxxService xxx;直接红。 - 运行期 :测试类仍是
@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 改属性形态,别臆造别名。
常见误判还有两类:
- 「我用的是
@WebMvcTest,是不是也要加?」 ------切片测试本身通常已带 MockMvc 相关自动配置;本篇主线是@SpringBootTest不再自动给 MockMvc。升级时以你实际用的测试注解为准,对照指南对应节,不要把所有测试类无差别堆上同一条注解。 - 「加了
@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 时的务实顺序:
- 列出被多个测试
@Import的旧 Configuration / TestConfiguration; - 把其中的
@MockBean/@SpyBean字段改写成一份@MockitoBean(types={...})或一个@SharedMocks; - 各测试类改为标注该组合注解(或直接类级
types=),删除字段挂载; - 跑受影响的测试子集,确认 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」:
- 搜
@MockBean/@SpyBean(含测试与@TestConfiguration、IT 命名)。 - 测试类字段 改成
@MockitoBean/@MockitoSpyBean;跑通单测模块编译。 - 凡用 MockMvc 的
@SpringBootTest,补@AutoConfigureMockMvc;HtmlUnit 开关改htmlUnit = @HtmlUnit(...)。 - 拆掉 Configuration 字段上的 mock ;改成
types=或@SharedMocks组合注解。 - 若还注入
WebClient/TestRestTemplate失败,对照 Migration Guide 对应节加 AutoConfigure(旁证,勿拖长本 PR 主线)。 - 回归:至少一条 Controller MockMvc 用例 + 一条带 mock 协作对象的切片/集成测试。
- 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」的噪音里漏掉「注解已经不存在」的编译根因。