SpringBoot 集成测试:@SpringBootTest 与 MockMvc

在 SpringBoot 实际开发中,我们写的单元测试(比如只测试 Service 层单个方法),往往只能覆盖局部逻辑,却忽略了一个关键问题:单个方法没问题,不代表整个调用链路没问题

比如:Controller 层的参数校验是否生效?拦截器是否正常拦截请求?Service 层调用 Mapper 层后数据返回是否符合预期?接口的异常处理是否正确?这些场景,单元测试无法覆盖,必须靠集成测试来验证。

一、集成测试 vs 单元测试

很多同学分不清单元测试和集成测试,导致测试写得冗余、无效,先用一张表彻底区分,后续写测试才不会跑偏:

对比维度 单元测试 SpringBoot 集成测试
测试范围 单个类、单个方法(如 Service 单个方法) 全链路(Controller → Service → Mapper → 数据库/第三方接口)
是否加载 Spring 上下文 不加载,靠 Mock 模拟依赖(如 Mock Service) 加载完整 Spring 上下文,Bean 真实注入
核心工具 JUnit、Mockito @SpringBootTest、MockMvc、TestContainers(可选)
测试速度 快(毫秒级),无需启动上下文 慢(秒级),需加载配置、启动上下文
适用场景 验证单个方法的逻辑正确性 验证接口全链路、配置生效、依赖调用正确性

简单总结:单元测试测「点」,保证单个方法不出错;集成测试测「线」,保证整个接口链路能正常跑通。两者结合,才能最大化保证代码质量。

而我们今天的主角:@SpringBootTest 是集成测试的「入口」,负责启动 Spring 上下文;MockMvc 是集成测试的「工具」,负责模拟 HTTP 请求,无需启动真实服务器就能测试接口。

二、环境准备:依赖配置 + 项目结构

首先明确:SpringBoot 2.2+ 版本后,默认集成了 spring-boot-starter-test 依赖,无需额外手动添加,但如果是老项目或依赖缺失,需手动补充,同时建议指定版本(和 SpringBoot 版本一致)。

2.1 核心依赖配置(pom.xml)

go 复制代码
<!-- SpringBoot 测试核心依赖(包含 JUnit 5、MockMvc、Mockito 等) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
    <!-- 若需指定版本,与 SpringBoot 版本保持一致 -->
    <!-- <version>2.7.10</version> -->
</dependency>

<!-- 可选:若测试中需要操作数据库,添加数据库依赖(示例 MySQL) -->
<dependency>
    <groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
    <scope>test</scope>
</dependency>

<!-- 可选:若需模拟第三方接口,添加 OkHttp 依赖 -->
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <scope>test</scope>
    <version>4.11.0</version>
</dependency>

2.2 项目测试结构

建议遵循 SpringBoot 测试规范,测试类与业务类包路径一致,便于维护,示例结构:

go 复制代码
com.example.demo
├── DemoApplication.java  // 项目启动类
├── controller
│   └── UserController.java  // 业务接口类
├── service
│   └── UserService.java     // 业务逻辑类
├── mapper
│   └── UserMapper.java      // 数据访问类
└── src/test/java
    └── com.example.demo
        ├── controller
        │   └── UserControllerTest.java  // Controller 层集成测试
        ├── service
        │   └── UserServiceTest.java     // Service 层集成测试(可混合单元测试)
        └── DemoApplicationTests.java    // 全局集成测试入口

三、 @SpringBootTest:

很多开发者用 @SpringBootTest 只知道加个注解就完事,却不知道它的底层原理和灵活配置,导致测试效率低、冗余。今天彻底拆解它的核心用法和进阶配置。

3.1 底层原理

@SpringBootTest 本质是一个「上下文启动器」,它会扫描项目中的 @SpringBootApplication 启动类,加载所有配置(application.yml/properties、配置类、Bean 定义),创建一个完整的 Spring 应用上下文,让测试类能像真实运行环境一样,注入所有 Bean(Service、Mapper、Controller 等)。

注意:它默认不会启动内嵌服务器(如 Tomcat),除非指定 webEnvironment 属性。

3.2 核心配置属性

@SpringBootTest 有多个配置属性,日常开发中最常用的有 3 个,结合场景灵活使用:

(1)classes:指定启动类

默认情况下,@SpringBootTest 会自动查找项目中唯一的 @SpringBootApplication 注解类作为启动类,无需手动指定。但如果项目中有多个启动类(如多模块项目),就必须手动指定,否则会报错。

go 复制代码
// 多模块项目中,手动指定启动类
@SpringBootTest(classes = DemoApplication.class)
public class UserControllerTest {
    // 测试代码
}
(2)webEnvironment:指定 Web 环境

这是最常用的属性,决定了测试的 Web 环境模式,有 4 个可选值,重点记前 3 个:

  • WebEnvironment.MOCK(默认):模拟 Web 环境,不启动真实内嵌服务器(Tomcat),适合配合 MockMvc 测试接口(最常用);

  • WebEnvironment.RANDOM_PORT:启动真实内嵌服务器,随机分配一个端口,适合测试真实 HTTP 请求(如测试拦截器、过滤器、跨域配置);

  • WebEnvironment.DEFINED_PORT:启动真实内嵌服务器,使用配置文件中指定的端口(如 application.yml 中的 server.port);

  • WebEnvironment.NONE:不启动 Web 环境,适合测试非 Web 项目(如纯 Service、Mapper 层测试)。

示例:使用随机端口启动真实服务器,测试真实 HTTP 请求:

go 复制代码
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class RealHttpTest {

    // 注入随机端口(通过 @LocalServerPort 注解)
    @LocalServerPort
    private int port;

    // 用 OkHttp 发起真实 HTTP 请求
    private OkHttpClient okHttpClient = new OkHttpClient();

    @Test
    public void testRealHttp() throws IOException {
        // 构建请求地址(随机端口)
        String url = "http://localhost:" + port + "/user/1";
        Request request = new Request.Builder().url(url).build();
        Response response = okHttpClient.newCall(request).execute();
        // 校验响应状态码
        Assertions.assertEquals(200, response.code());
    }
}
(3)properties:临时覆盖配置

测试环境中,我们可能需要临时覆盖配置文件中的参数(如数据库地址、第三方接口地址),无需修改 application.yml,直接通过 properties 属性指定即可,优先级高于配置文件。

go 复制代码
// 临时覆盖数据库地址、日志级别,仅在测试中生效
@SpringBootTest(properties = {
        "spring.datasource.url=jdbc:mysql://localhost:3306/test_db",
        "logging.level.com.example.demo=DEBUG"
})
public class ConfigOverrideTest {
    // 测试代码
}

3.3 基础使用示例(Service 层集成测试)

集成测试中,我们可以直接注入 Service、Mapper 等 Bean,测试业务逻辑的完整调用(包括数据库操作),示例:

go 复制代码
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
public class UserServiceIntegrationTest {

    // 直接注入真实的 UserService(非 Mock)
    @Autowired
    private UserService userService;

    // 直接注入真实的 UserMapper
    @Autowired
    private UserMapper userMapper;

    /**
     * 测试:根据 ID 查询用户(全链路:Service → Mapper → 数据库)
     */
    @Test
    public void testGetUserById() {
        // 1. 先插入测试数据(避免依赖数据库原有数据)
        User testUser = new User(null, "测试用户", 25, "13800138000");
        userMapper.insert(testUser);
        Long userId = testUser.getId();

        // 2. 调用 Service 方法
        User resultUser = userService.getUserById(userId);

        // 3. 校验结果
        assertNotNull(resultUser);
        assertEquals("测试用户", resultUser.getUsername());
        assertEquals(25, resultUser.getAge());

        // 4. 清理测试数据(避免污染数据库)
        userMapper.deleteById(userId);
    }
}

注意:这里没有用 Mock,而是直接操作真实数据库,适合验证数据层和业务层的联动正确性。

四、MockMvc 进阶

MockMvc 是 Spring Test 提供的 HTTP 请求模拟工具,核心优势是:无需启动真实服务器,就能模拟 GET、POST、PUT、DELETE 等所有 HTTP 请求,并精准校验响应结果(状态码、响应体、响应头、Cookie 等),是 Controller 层集成测试的必备工具。

很多开发者只用过它的基础方法,今天分享实战中高频的进阶用法,覆盖大部分接口测试场景。

4.1 两种初始化方式

MockMvc 的初始化有两种方式,根据场景选择,推荐第一种(简单高效):

方式1:@AutoConfigureMockMvc 自动注入

在 @SpringBootTest 注解下,添加 @AutoConfigureMockMvc 注解,Spring 会自动配置 MockMvc 实例,直接注入即可使用,无需手动构建,适合大多数场景。

go 复制代码
@SpringBootTest
@AutoConfigureMockMvc // 自动配置 MockMvc
public class UserControllerTest {

    // 直接注入 MockMvc 实例
    @Autowired
    private MockMvc mockMvc;

    // 测试方法...
}
方式2:手动构建

如果需要自定义 MockMvc 配置(如添加拦截器、过滤器、异常处理器),可以通过 WebApplicationContext 手动构建,灵活度更高。

go 复制代码
@SpringBootTest
public class CustomMockMvcTest {

    private MockMvc mockMvc;

    // 注入 Web 应用上下文
    @Autowired
    private WebApplicationContext webApplicationContext;

    // 测试方法执行前,初始化 MockMvc(@BeforeEach 是 JUnit 5 注解,替代 JUnit 4 的 @Before)
    @BeforeEach
    public void setUp() {
        // 手动构建 MockMvc,可添加自定义配置(如添加拦截器)
        mockMvc = MockMvcBuilders
                .webAppContextSetup(webApplicationContext)
                // 模拟文件上传(可选)
                .addFileUpload(new MockMultipartFile("file", "test.txt", "text/plain", "test content".getBytes()))
                // 全局异常处理(可选,若项目有自定义异常处理器,可省略)
                .setControllerAdvice(new GlobalExceptionHandler())
                .build();
    }

    // 测试方法...
}

4.2 核心方法详解

MockMvc 的所有操作都遵循「发起请求 → 配置请求 → 校验响应」的固定套路,核心方法整理如下,结合示例理解,记熟就能直接用:

(1)发起请求:perform()

所有请求的入口,参数是一个 RequestBuilder 对象,常用的 RequestBuilder 由 MockMvcRequestBuilders 提供,如 get()、post()、put()、delete()。

go 复制代码
// 发起 GET 请求
mockMvc.perform(get("/user/1"));

// 发起 POST 请求
mockMvc.perform(post("/user/add"));
(2)配置请求:设置请求参数、请求体、请求头

根据接口类型,配置请求的参数、请求体、请求头等,覆盖所有常见场景:

go 复制代码
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.http.MediaType.*;

// 1. GET 请求:传递路径参数 + 请求参数 + 请求头
mockMvc.perform(get("/user/{id}", 1) // 路径参数({id} 对应后面的 1)
        .param("type", "1") // 请求参数(?type=1)
        .header("token", "test-token-123") // 请求头
        .cookie(new Cookie("userId", "123"))); // Cookie

// 2. POST 请求:传递 JSON 请求体 + 请求头
mockMvc.perform(post("/user/add")
        .contentType(APPLICATION_JSON) // 设置请求类型为 JSON
        .accept(APPLICATION_JSON) // 设置接收的响应类型为 JSON
        .content("{\"username\":\"测试用户\",\"age\":25}")); // JSON 请求体

// 3. POST 请求:传递表单参数
mockMvc.perform(post("/user/login")
        .contentType(APPLICATION_FORM_URLENCODED) // 表单类型
        .param("username", "admin")
        .param("password", "123456"));

// 4. 上传文件(配合手动构建 MockMvc 时的 addFileUpload)
mockMvc.perform(multipart("/user/upload")
        .file("file", "test content".getBytes())
        .param("desc", "测试文件"));
(3)校验响应:andExpect()

最核心的方法,用于校验响应结果,可校验状态码、响应体、响应头、Cookie 等,常用的校验器由 MockMvcResultMatchers 提供。

go 复制代码
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

mockMvc.perform(get("/user/1"))
        // 1. 校验响应状态码(200 成功、404 不存在、500 服务器错误等)
        .andExpect(status().isOk()) // 等价于 status().is(200)
        // 2. 校验响应头
        .andExpect(header().string("Content-Type", "application/json"))
        // 3. 校验 Cookie
        .andExpect(cookie().value("userId", "123"))
        // 4. 校验响应体(JSON 格式)
        .andExpect(jsonPath("$.id").value(1)) // 校验 JSON 字段 id = 1
        .andExpect(jsonPath("$.username").value("测试用户")) // 校验 username
        .andExpect(jsonPath("$.age").isNumber()) // 校验 age 是数字
        // 5. 校验响应体(普通文本格式)
        // .andExpect(content().string("操作成功"))
        ;

注意:jsonPath 表达式的用法和前端的 JSONPath 一致,比如 $.data.list 表示响应体中 data 对象下的 list 数组,非常灵活。

(4)辅助方法:andDo()、andReturn()

除了校验,还有两个常用辅助方法,用于调试和获取响应结果:

go 复制代码
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;

// 1. andDo(print()):打印完整的请求/响应日志(调试必备)
mockMvc.perform(get("/user/1"))
        .andDo(print()) // 打印请求地址、请求头、响应体等信息
        .andExpect(status().isOk());

// 2. andReturn():获取响应结果,用于后续自定义校验
MvcResult mvcResult = mockMvc.perform(get("/user/1"))
        .andExpect(status().isOk())
        .andReturn();

// 获取响应体字符串
String responseBody = mvcResult.getResponse().getContentAsString();
// 自定义校验(比如解析 JSON 后校验复杂逻辑)
User user = new ObjectMapper().readValue(responseBody, User.class);
Assertions.assertEquals(25, user.getAge());

4.3 实战场景:覆盖 90% 的接口测试需求

结合实际开发中的接口类型,分享 4 个高频实战场景,代码可直接复制到项目中修改使用。

场景1:测试 GET 接口(路径参数 + 请求参数)

假设我们有一个用户列表接口,支持分页查询,路径:/user/list,参数:pageNum(页码)、pageSize(每页条数),返回分页结果。

go 复制代码
@Test
public void testUserList() throws Exception {
    mockMvc.perform(get("/user/list")
            .param("pageNum", "1")
            .param("pageSize", "10")
            .header("token", "test-token"))
            .andDo(print())
            .andExpect(status().isOk())
            // 校验分页结果的核心字段
            .andExpect(jsonPath("$.code").value(200))
            .andExpect(jsonPath("$.message").value("success"))
            .andExpect(jsonPath("$.data.list").isArray()) // list 是数组
            .andExpect(jsonPath("$.data.pageNum").value(1))
            .andExpect(jsonPath("$.data.pageSize").value(10));
}
场景2:测试 POST JSON 接口(请求体 + 异常校验)

假设我们有一个添加用户接口,路径:/user/add,请求体是 JSON,要求 username 不能为空、age 必须大于 0,否则返回 400 错误。

go 复制代码
@Test
public void testAddUser_Success() throws Exception {
    // 构建合法的 JSON 请求体
    User user = new User(null, "新用户", 22, "13800138000");
    String json = new ObjectMapper().writeValueAsString(user);

    mockMvc.perform(post("/user/add")
            .contentType(APPLICATION_JSON)
            .content(json))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200))
            .andExpect(jsonPath("$.message").value("添加成功"));
}

// 测试异常场景:username 为空
@Test
public void testAddUser_Error_UsernameNull() throws Exception {
    User user = new User(null, "", 22, "13800138000");
    String json = new ObjectMapper().writeValueAsString(user);

    mockMvc.perform(post("/user/add")
            .contentType(APPLICATION_JSON)
            .content(json))
            .andDo(print())
            .andExpect(status().isBadRequest()) // 400 错误
            .andExpect(jsonPath("$.code").value(400))
            .andExpect(jsonPath("$.message").contains("username 不能为空"));
}
场景3:测试拦截器(验证 token 有效性)

假设项目中有一个登录拦截器,未携带 token 或 token 无效时,拦截请求并返回 401 错误。

go 复制代码
// 测试:未携带 token,被拦截
@Test
public void testWithoutToken() throws Exception {
    mockMvc.perform(get("/user/1"))
            .andDo(print())
            .andExpect(status().isUnauthorized()) // 401 未授权
            .andExpect(jsonPath("$.code").value(401))
            .andExpect(jsonPath("$.message").value("请先登录"));
}

// 测试:携带无效 token,被拦截
@Test
public void testInvalidToken() throws Exception {
    mockMvc.perform(get("/user/1")
            .header("token", "invalid-token"))
            .andDo(print())
            .andExpect(status().isUnauthorized())
            .andExpect(jsonPath("$.message").value("token 无效"));
}
场景4:测试文件上传接口

假设我们有一个文件上传接口,路径:/user/upload,接收文件和描述参数,返回上传结果。

go 复制代码
@Test
public void testFileUpload() throws Exception {
    // 构建模拟文件
    MockMultipartFile file = new MockMultipartFile(
            "file", // 表单字段名
            "test.jpg", // 文件名
            "image/jpeg", // 文件类型
            "test image content".getBytes() // 文件内容
    );

    mockMvc.perform(multipart("/user/upload")
            .file(file)
            .param("desc", "测试图片"))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200))
            .andExpect(jsonPath("$.data.filename").value("test.jpg"));
}

五、@SpringBootTest + MockMvc 组合

实际开发中,我们不会单独使用 @SpringBootTest 或 MockMvc,而是将两者组合,实现「启动完整上下文 + 模拟接口请求 + 校验业务逻辑」的全链路测试,覆盖 Controller → Service → Mapper → 数据库的完整流程。

5.1 完整测试类示例

go 复制代码
import com.fasterxml.jackson.databind.ObjectMapper;
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.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest // 启动完整上下文
@AutoConfigureMockMvc // 自动配置 MockMvc
public class UserFullLinkIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private UserService userService;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 全链路测试:接口请求 → Service 处理 → Mapper 操作数据库 → 响应结果
     */
    @Test
    public void testUserFullLink() throws Exception {
        // 第一步:插入测试数据(模拟数据库已有数据)
        User testUser = new User(null, "全链路测试用户", 30, "13900139000");
        userMapper.insert(testUser);
        Long userId = testUser.getId();

        // 第二步:用 MockMvc 模拟接口请求
        mockMvc.perform(get("/user/{id}", userId)
                .header("token", "test-token-123"))
                .andDo(print())
                // 第三步:校验接口响应
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.id").value(userId))
                .andExpect(jsonPath("$.username").value("全链路测试用户"))
                .andExpect(jsonPath("$.age").value(30));

        // 第四步:校验 Service 层逻辑(确保上下文加载正常,Bean 注入有效)
        User serviceUser = userService.getUserById(userId);
        assertNotNull(serviceUser);
        assertEquals("全链路测试用户", serviceUser.getUsername());

        // 第五步:清理测试数据(避免污染数据库)
        userMapper.deleteById(userId);
    }
}

5.2 测试数据隔离

上面的示例中,我们手动插入和删除测试数据,虽然能实现隔离,但比较繁琐。实际开发中,推荐使用 @Transactional 注解,让测试方法执行完自动回滚数据,无需手动清理。

go 复制代码
// 添加 @Transactional 注解,测试结束后自动回滚所有数据库操作
@Test
@Transactional
public void testWithTransaction() throws Exception {
    // 插入测试数据
    User testUser = new User(null, "事务测试用户", 28, "13700137000");
    userMapper.insert(testUser);
    Long userId = testUser.getId();

    // 接口请求 + 校验
    mockMvc.perform(get("/user/{id}", userId))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.username").value("事务测试用户"));

    // 无需手动删除,测试结束后自动回滚
}

注意:@Transactional 注解仅对数据库操作有效,若测试中调用了第三方接口(如支付、短信),不会回滚第三方接口的操作,需手动模拟或清理。

六、注意事项

很多开发者写集成测试时,会遇到各种奇奇怪怪的问题,整理了 10 个高频坑点,帮你少走弯路:

    1. @SpringBootTest 启动慢 → 原因:加载了完整上下文,若项目较大,启动时间会很长。解决方案:按需加载上下文,用 @TestConfiguration 自定义测试配置,只加载需要的 Bean;
    1. MockMvc 报错 404 → 原因:1. 接口路径写错(注意是否有前缀,如 /api/user);2. Controller 未被扫描到(包路径不一致);3. 未添加 @AutoConfigureMockMvc 注解;
    1. 注入 Bean 失败(报 NoSuchBeanDefinitionException) → 原因:@SpringBootTest 未找到启动类,或 Bean 未加 @Service/@Controller/@Repository 注解;
    1. 测试数据污染数据库 → 解决方案:添加 @Transactional 注解自动回滚,或使用 TestContainers 启动临时数据库;
    1. MockMvc 无法模拟文件上传 → 原因:未用 multipart() 方法,或未手动构建 MockMvc 并添加文件上传配置;
    1. jsonPath 校验报错(Invalid JSON expression) → 原因:JSON 路径写错,或响应体不是 JSON 格式(检查请求头 Content-Type 是否为 application/json);
    1. @LocalServerPort 注入失败 → 原因:未设置 webEnvironment = WebEnvironment.RANDOM_PORT/DEFINED_PORT,只有启动真实服务器时才能注入端口;
    1. 测试方法执行顺序混乱 → 原因:JUnit 5 默认随机执行测试方法。解决方案:用 @Order 注解指定执行顺序;
    1. 依赖第三方接口的测试不稳定 → 解决方案:用 Mockito 模拟第三方接口的返回,避免依赖真实第三方服务;
    1. 集成测试和单元测试混写 → 建议:分开写,单元测试放在 service/test 下,集成测试放在 controller/test 下,命名规范(如 XxxServiceTest、XxxControllerIntegrationTest)。

七、让集成测试更高效

除了基础用法,分享 3 个优化技巧,让你的集成测试更高效、更易维护:

7.1 静态导入简化代码

将 MockMvc 的常用方法静态导入,避免重复写全类名,代码更简洁:

go 复制代码
// 静态导入(放在类顶部)
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;

// 简化后代码
mockMvc.perform(get("/user/1"))
        .andDo(print())
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id").value(1));

7.2 抽取测试基类

如果多个测试类都需要注入 MockMvc、ObjectMapper 等,可抽取一个测试基类,避免重复代码:

go 复制代码
// 测试基类
@SpringBootTest
@AutoConfigureMockMvc
public class BaseIntegrationTest {
    @Autowired
    protected MockMvc mockMvc;

    @Autowired
    protected ObjectMapper objectMapper;

    // 可添加通用方法,如构建 JSON 请求体
    protected String toJson(Object obj) throws JsonProcessingException {
        return objectMapper.writeValueAsString(obj);
    }
}

// 其他测试类继承基类
public class UserControllerTest extends BaseIntegrationTest {
    @Test
    public void testUser() throws Exception {
        User user = new User(null, "测试", 25);
        mockMvc.perform(post("/user/add")
                .contentType(APPLICATION_JSON)
                .content(toJson(user)))
                .andExpect(status().isOk());
    }
}

7.3 用 TestContainers 启动临时数据库

如果测试需要操作数据库,而本地数据库环境不一致,可使用 TestContainers 启动一个临时的 MySQL/PostgreSQL 容器,测试结束后自动销毁,避免环境依赖。(需添加对应依赖,这里不展开,感兴趣可留言)

八、总结

SpringBoot 集成测试的核心,就是用 @SpringBootTest 加载完整上下文,用 MockMvc 模拟 HTTP 请求,两者组合,实现接口全链路的验证。

最后再梳理核心要点,帮你快速记忆:

集成测试不是额外的负担,而是提前发现问题的重要手段。掌握 @SpringBootTest 和 MockMvc 的用法,能让你在开发中更有底气,上线更安心。

如果觉得本文对你有帮助,欢迎点赞、在看、转发,关注我,持续分享 Java 实战干货~

相关推荐
enAn_2 小时前
对照片和视频文件名,程序追加日期,直观看
java·maven
uzong2 小时前
软件人员可以关注的 Skill,亲测确实不错,值得试一下
人工智能·后端
掘金虾2 小时前
Hono 框架入门到实战:用 Node.js 写一个支持工具调用的流式对话 Agent
后端
用户8356290780512 小时前
Python 自动拆分 Word 文档教程:按分节符与分页符处理
后端·python
yaaakaaang2 小时前
六、适配器模式
java·适配器模式
树獭叔叔2 小时前
Claude Code 工具系统深度剖析:从静态注册到动态发现
后端·aigc·openai
bobasyu2 小时前
Claude Code 源码笔记 -- queryLoop
java·笔记·spring
树獭叔叔2 小时前
Claude Code 的上下文管理:多层渐进式压缩架构深度解析
后端·aigc·openai
计算机学姐2 小时前
基于SpringBoot的高校竞赛管理系统
java·spring boot·后端·spring·信息可视化·tomcat·mybatis