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」的噪音里漏掉「注解已经不存在」的编译根因。

相关推荐
ZealSinger5 天前
Boot4升后Kafka retry属性怎么改
kafka·retry·boot4·升级踩坑
ZealSinger7 天前
Boot4升后session_redis属性怎么改名
redis·springsession·boot4·升级踩坑
ZealSinger11 天前
Boot4升后HttpServiceClient怎么换
boot4·httpservice·importhttp·升级踩坑
ZealSinger15 天前
Boot4升后WebClient starter怎么换
starter·webclient·restclient·boot4
LXMXHJ2 年前
项目测试之MockMvc
mockmvc
深色風信子2 年前
SpringBoot MockMvc
spring boot·mockmvc·springboot 打桩
Python南帝3 年前
Spring Boot单元测试
spring boot·单元测试·spring boot单元测试·mockmvc·spring boot 单元·controller层单元测试·postman基本用法