Spring Boot测试终极指南:从环境搭建到分层测试实战

掌握MockMvc与分层测试策略,让你的代码质量提升一个维度

一、环境搭建:Maven依赖深度解析

Spring Boot测试的核心依赖在pom.xml中配置如下:

<dependencies><!-- 核心测试库:包含JUnit 5、Mockito、AssertJ等 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- MockMvc独立测试支持 --><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><scope>test</scope></dependency><!-- AssertJ流式断言库 --><dependency><groupId>org.assertj</groupId><artifactId>assertj-core</artifactId><version>3.24.2</version><scope>test</scope></dependency>
</dependencies>
关键依赖详解:
  • spring-boot-starter-test:测试核心包,包含:
    • JUnit Jupiter(JUnit 5测试引擎)
    • Mockito(模拟依赖对象)
    • JSONPath(JSON数据解析)
    • AssertJ(流式断言)
  • spring-test:提供MockMvc等Spring测试工具类
  • assertj-core:增强版断言库,支持链式调用

为什么选择JUnit 5而不是JUnit 4?
JUnit 5的模块化架构(Jupiter+Platform+Vintage)支持Lambda表达式、参数化测试等新特性,且与Spring Boot 2.2+深度集成:

// JUnit 5示例(支持Lambda)
@Test
@DisplayName("测试商品价格计算")
void testPriceCalculation() {assertAll(() -> assertThat(calculator.calculate(10)).isEqualTo(100),() -> assertThatThrownBy(() -> calculator.calculate(-1)).isInstanceOf(IllegalArgumentException.class));
}

二、核心概念深度剖析

1. 应用上下文(Application Context)

Spring容器核心,管理Bean的生命周期和依赖注入:

@SpringBootTest
public class ContextLoadTest {@Autowiredprivate ApplicationContext ctx; // 注入应用上下文@Testvoid testBeanExistence() {assertThat(ctx.containsBean("productService")).isTrue();}
}

通过@SpringBootTest加载完整上下文,适合集成测试

2. 断言(Assertions)

验证代码行为的检查点,分为:

  • 基础断言:验证true/false、相等性等
    // JUnit基础断言
    assertEquals(expected, actual);
    
  • 流式断言(AssertJ):提升可读性
    // AssertJ链式断言
    assertThat(product).hasFieldOrProperty("name").hasFieldOrPropertyWithValue("price", 99.9).extracting(Product::getCategory).isEqualTo("电子产品");
    

AssertJ的错误信息更直观,支持集合、异常等复杂验证

三、MockMvc全解:Controller层隔离测试

1. 核心组件
  • MockMvc:模拟HTTP请求的工具
  • standaloneSetup:独立构建控制器,不加载完整上下文
    mockMvc = MockMvcBuilders.standaloneSetup(productController).build();
    
    适用于纯Controller逻辑测试,避免加载无关Bean
2. HTTP请求模拟链
// 测试商品查询接口
mockMvc.perform(get("/api/products")   // ① 模拟GET请求.param("category", "电子")     // 添加查询参数.contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk())         // ② 验证HTTP状态码.andExpect(jsonPath("$[0].id").value(1001)) // ③ JSONPath验证.andDo(print());                    // ④ 打印请求详情
  • perform():发起HTTP请求(支持GET/POST/PUT/DELETE)
  • andExpect():验证响应结果(状态码、Header、Body)
  • jsonPath():使用JSONPath语法定位JSON字段
    // 验证返回的JSON数组中第一个元素的name字段
    .andExpect(jsonPath("$[0].name").value("华为手机"))
    
  • andDo():执行附加操作(如打印日志、保存结果)
3. 请求体处理
// 测试新增商品
ProductQuery query = new ProductQuery("手机", 1000, 2000); // ① 构建查询对象
String json = objectMapper.writeValueAsString(query);    // ② 转为JSONmockMvc.perform(post("/api/products").content(json).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isCreated());

ProductQuery参数封装对象,用于接收查询条件

四、分层测试策略:Controller vs Service

1. 为什么分层测试?
测试类型测试目标使用注解特点
ControllerHTTP接口逻辑@WebMvcTest模拟HTTP请求,不启动完整服务
Service业务逻辑正确性@SpringBootTest加载完整上下文,测试真实逻辑
Repository数据访问层@DataJpaTest使用内存数据库

分层测试实现关注点分离,避免测试复杂度爆炸

2. Controller层测试示例
@WebMvcTest(ProductController.class) // 只加载Controller相关Bean
public class ProductControllerTest {@Autowiredprivate MockMvc mockMvc;@MockBeanprivate ProductService productService; // 模拟Service@Testvoid testGetProduct() throws Exception {// 模拟Service返回when(productService.findById(1001)).thenReturn(new Product(1001, "测试商品"));mockMvc.perform(get("/products/1001")).andExpect(jsonPath("$.name").value("测试商品"));}
}
3. Service层测试示例
@SpringBootTest
public class ProductServiceTest {@Autowiredprivate ProductService service;  // 真实Service@MockBeanprivate ProductRepository repo; // 模拟Repository@Testvoid testCreateProduct() {Product product = new Product("新品");when(repo.save(any())).thenReturn(product);Product created = service.create(product);assertThat(created.getName()).isEqualTo("新品");}
}

五、高级技巧:数据准备与验证增强

1. @Sql注解:测试数据初始化
@Test
@Sql(scripts = "/init-products.sql",   // ① 初始化脚本config = @SqlConfig(transactionMode = ISOLATED))
@Sql(scripts = "/cleanup.sql",          // ② 清理脚本executionPhase = AFTER_TEST_METHOD)
public void testProductCount() {int count = service.countProducts();assertThat(count).isEqualTo(10);
}
  • 脚本路径:src/test/resources/init-products.sql
  • 执行阶段:BEFORE_TEST_METHOD(默认)或AFTER_TEST_METHOD
2. AssertJ集合断言
List<Product> products = service.search("手机");assertThat(products).hasSize(3).extracting(Product::getPrice).allMatch(price -> price > 1000);

六、测试架构最佳实践

1. 分层测试金字塔
UI Tests
Controller Tests
Service Tests
Repository Tests
  • 底层测试(Service/Repository)数量最多
  • 顶层测试(Controller)覆盖关键接口
2. 测试数据管理策略
方式适用场景示例
内存数据库Repository层测试H2 + @DataJpaTest
@Sql初始化固定数据场景@Sql("/init-data.sql")
Mockito动态生成无需持久化的数据when(repo.findAll()).thenReturn(list)

七、Demo 测试类(完整版)​​

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.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;@SpringBootTest
@AutoConfigureMockMvc
@Sql(scripts = "/test-data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) // 测试前执行SQL初始化数据[8](@ref)
@Sql(scripts = "/clean-data.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 测试后清理数据[8](@ref)
public class ProductControllerTest {@Autowiredprivate MockMvc mockMvc;@MockBeanprivate ProductService productService; // 模拟Service层// ========================= 增删改查测试 =========================@Testvoid testCreateProduct() throws Exception {String jsonBody = "{\"name\":\"MacBook Pro\",\"price\":12999}";mockMvc.perform(MockMvcRequestBuilders.post("/products").contentType(MediaType.APPLICATION_JSON).content(jsonBody)).andExpect(status().isCreated()) // 断言HTTP 201.andExpect(jsonPath("$.id").exists()) // 验证返回的JSON有id字段[4](@ref).andDo(print()); // 打印请求/响应详情[7](@ref)}@Testvoid testGetProductById() throws Exception {// 模拟Service返回数据when(productService.findById(1001L)).thenReturn(new Product(1001L, "iPhone 15", 7999));mockMvc.perform(MockMvcRequestBuilders.get("/products/1001")).andExpect(status().isOk()).andExpect(jsonPath("$.name").value("iPhone 15")) // JSONPath验证字段值[1](@ref).andExpect(jsonPath("$.price").value(7999));}@Testvoid testBatchCreateProducts() throws Exception {String jsonArray = """[{"name":"iPad Air", "price":4499},{"name":"Apple Watch", "price":2999}]""";mockMvc.perform(MockMvcRequestBuilders.post("/products/batch").contentType(MediaType.APPLICATION_JSON).content(jsonArray)).andExpect(status().isCreated()).andExpect(jsonPath("$.length()").value(2)); // 验证返回数组长度[4](@ref)}// ========================= 文件上传测试 =========================@Testvoid testUploadProductList() throws Exception {// 构建CSV模拟文件[9,11](@ref)String csvContent = "id,name,price\n101,Keyboard,199\n102,Mouse,99";MockMultipartFile file = new MockMultipartFile("file", // 参数名(必须与@RequestParam一致)"products.csv", // 文件名"text/csv", // 文件类型csvContent.getBytes() // 文件内容);mockMvc.perform(MockMvcRequestBuilders.multipart("/products/upload").file(file).param("source", "excel")) // 附加普通参数.andExpect(status().isOk()).andExpect(content().string("2 records imported"));}// ========================= 删除测试 =========================@Testvoid testDeleteProduct() throws Exception {mockMvc.perform(MockMvcRequestBuilders.delete("/products/1001")).andExpect(status().isNoContent()); // 204状态码[1](@ref)}
}

总结

通过MockMvc实现Controller层隔离测试,配合分层策略和AssertJ断言,可构建高效的测试体系。关键实践:

  1. 使用@WebMvcTest + MockMvc测试Controller,不启动Web服务器
  2. Service层用@SpringBootTest + @MockBean进行集成测试
  3. 利用JSONPath高效验证复杂JSON响应
  4. 通过@Sql管理测试数据生命周期

避坑指南:避免在Controller测试中加载完整上下文(@SpringBootTest),否则会导致测试速度下降10倍以上

实战项目源码下载 | Spring官方测试指南

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/910053.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/910053.shtml
英文地址,请注明出处:http://en.pswp.cn/news/910053.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

卷积类型总结

1. 标准卷积 (Convolution) 原理&#xff1a; 一个包含 K 个滤波器的卷积层&#xff08;每个滤波器大小为 FxF x C_in&#xff09;在输入特征图上滑动。在每个位置&#xff0c;滤波器与输入图像的局部区域进行逐元素相乘再求和&#xff08;点积运算&#xff09;&#xff0c;得到…

HP iLO使用实例:Integrated Lights-Out

文章目录 参考名词解释iLODMTF SMASH CLP命令HP提供的命令VSPBIOS启动顺序BIOS指令启动时报的电源错误如果ilo登陆不上去参考 ilo命令 https://support.hpe.com/hpesc/public/docDisplay?docId=c03334058 名词解释 iLO 从字面理解:Integrated Lights-Out,就是整合灯出;…

【前端隐蔽 Bug 深度剖析:SVG 组件复用中的 ID 冲突陷阱】

前端隐蔽 Bug 深度剖析&#xff1a;SVG 组件复用中的 ID 冲突陷阱 创建时间: 2025/6/20 类型: &#x1f50d; Bug 深度分析 难度: ⭐⭐⭐⭐⭐ 高级 关键词: SVG、ID 冲突、Vue 组件、隐蔽 Bug、技术分析 &#x1f4d6; 引言 在前端开发的世界里&#xff0c;有一类 Bug 特别令…

IDEA 中 Tomcat 部署 Java Web 项目(Maven 多模块 非 Maven 通用版)

引言 Java Web 开发中&#xff0c;Tomcat 是最常用的 Servlet 容器&#xff0c;而项目类型通常分为 Maven 管理&#xff08;依赖自动处理、多模块聚合&#xff09; 和 非 Maven 纯手工管理&#xff08;手动引入 jar 包、配置项目结构&#xff09;。本文覆盖 两种项目类型 的 T…

当建筑学会“行走”:MiC建筑如何重塑医疗空间

当塔吊林立的工地被智能化工厂取代&#xff0c;当混凝土现浇的轰鸣被精密模块的无声拼接覆盖&#xff0c;建筑行业正经历着自钢筋混凝土革命以来最深刻的范式转移。模块化集成建筑&#xff08;MiC&#xff09;技术&#xff0c;这场以“制造组装”为核心的新型工业化浪潮&#x…

计算机网络八股第二期

计算机网络八股第二期 1.讲一讲从输入网址到网页显示之间发生了什么&#xff08;从网络的角度&#xff09; 想想一下你从网店买一本书&#xff0c;从输入网址到网页显示其实和你从网店买一本书差不多&#xff0c;网店发给你的是实体而网络传输的是文字&#xff0c;图片等等资…

FPGA基础 -- Verilog 的值变转储文件(VCD:Value Change Dump)

Verilog 的“值变转储文件&#xff08;VCD&#xff1a;Value Change Dump&#xff09;”&#xff0c;这是一项在仿真调试中至关重要的技术&#xff0c;可以帮助你“看见”RTL中每个信号随时间的变化过程。 一、什么是 Verilog 的值变转储文件&#xff08;VCD&#xff09; VCD …

流水灯芯片74HC138 → 74HC164 → 74HC595核心功能、引脚作用及芯片定位

一、74HC138&#xff1a;精准的“8选1开关” 核心作用 用3根控制线&#xff08;A0-A2&#xff09;实现8路严格单选&#xff0c;同一时刻仅1路输出低电平&#xff08;其他7路高电平&#xff09;。 &#x1f4cc; 本质&#xff1a;二进制地址译码器&#xff0c;不是数据寄存器。…

鸿蒙Next仓颉语言开发实战教程:懒加载

今天要分享的是仓颉开发语言中的懒加载。 先和初学者朋友们解释一下什么是懒加载。懒加载在代码中叫做LazyForEach&#xff0c;看到名字你一定能猜到它和ForEach的功能类似。只不过和ForEach的一次性加载所有数据不同&#xff0c;懒加载会根据屏幕可使区域按需加载数据&#x…

Linux 内核同步管理全解:原理 + 实战 + 考点

&#x1f525; 推荐&#xff1a;《Yocto项目实战教程&#xff1a;高效定制嵌入式Linux系统》 京东正版促销&#xff0c;欢迎支持原创&#xff01; 链接&#xff1a;https://item.jd.com/15020438.html 一、为什么需要同步机制&#xff1f; Linux 是一个支持 多核并发 抢占式调…

效果成本双突破!快手提出端到端生成式推荐系统OneRec!

近日&#xff0c;快手推荐模型团队提出了一个端到端生成式推荐系统OneRec&#xff0c;该系统采用Encoder-Decoder架构&#xff0c;引入了基于奖励机制的偏好对齐方法&#xff0c;借助强化学习增强模型效果&#xff0c;可在奖励模型引导下直接生成契合用户偏好的视频内容。通过极…

flex布局 项目属性

<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>flex布局 项目属性</title> <link href"css/k.css" rel"stylesheet" /> </head> <bod…

SpringBoot扩展——应用Web Service!

应用Web Service Web Service是一个SOA&#xff08;面向服务的编程&#xff09;架构&#xff0c;这种架构不依赖于语言&#xff0c;不依赖于平台&#xff0c;可以在不同的语言之间相互调用&#xff0c;通过Internet实现基于HTTP的网络应用间的交互调用。Web Service是一个可以…

EasyExcel学习笔记

EasyExcel学习 一、EasyExcel简介 一、EasyExcel是什么 EasyExcel是一个基于Java的简单、省内存的读写Excel的阿里开源项目。在尽可能节约内存的情况下支持读写百M的Excel。 官网&#xff1a;https://easyexcel.opensource.alibaba.com/ 学习Easyexcel前需要了解导入和导出…

day4课程

1整体认识和路由配置 2二级分类面包屑导航实现 3基础商品列表渲染 4列表筛选功能实现 5列表无限加载功能实现 6定制路由滚动行为 7详情页整体认识和路由配置 8详情页基础数据渲染 9详情页基础组件封装和数据渲染 10适配不同title和数据列表 11小图切换大图 12滑块跟随鼠标移动 …

kubeadm worker节点加入master失败

文章目录 1、操作2、问题现象3、问题原因4、问题解决4.1、重新生成token4.2、重新生成hash值 5、验证 1、操作 执行以下命令&#xff0c;让worker节点加入到master节点 kubeadm join 103.123.222.241:6443 --token vxe3v1.wzpnks8v1vbbtsu0 --discovery-token-ca-cert-hash s…

二十二、【用户管理与权限 - 篇四】后端权限定义:模型与 API 实现

【用户管理与权限 - 篇四】后端权限定义:模型与 API 实现 前言准备工作第一部分:设计并创建 `Permission` 模型第二部分:更新 `Role` 模型以关联 `Permission`第三部分:生成并应用数据库迁移第四部分:创建 Serializers第五部分:创建 ViewSets第六部分:注册 API 路由第七…

猜数字小游戏微信流量主小程序开源

这个智力小游戏采用了数字华容道的玩法&#xff0c;玩家需要通过移动数字方块&#xff0c;将数字按顺序排列完成游戏。代码严格遵循微信小程序的目录结构&#xff0c;包含以下部分&#xff1a; 完整的小程序配置文件&#xff08;app.js、app.json、app.wxss&#xff09; 游戏页…

探秘阿里云EBS存储:云计算的存储基石

一、引言 在云计算时代&#xff0c;数据如同企业的生命线&#xff0c;数据存储的重要性不言而喻。随着企业数字化转型的加速&#xff0c;海量数据的存储与高效管理成为亟待解决的问题。云存储以其卓越的灵活性、可扩展性和成本效益&#xff0c;逐渐成为众多企业的首选方案。在…

音视频之H.264的可伸缩编码SVC

系列文章&#xff1a; 1、音视频之视频压缩技术及数字视频综述 2、音视频之视频压缩编码的基本原理 3、音视频之H.264/AVC编码器原理 4、音视频之H.264的句法和语义 5、音视频之H.264/AVC解码器的原理和实现 6、音视频之H.264视频编码传输及其在移动通信中的应用 7、音视…