公司网站开发款记什么科目,怎么在工商局网站查公司,如何在网站做404页面,wordpress 后台空白Spring Boot测试终极指南#xff1a;从环境搭建到分层测试实战 掌握MockMvc与分层测试策略#xff0c;让你的代码质量提升一个维度 一、环境搭建#xff1a;Maven依赖深度解析
Spring Boot测试的核心依赖在pom.xml中配置如下#xff1a;
dependencies!-- 核心…Spring Boot测试终极指南从环境搭建到分层测试实战 掌握MockMvc与分层测试策略让你的代码质量提升一个维度 一、环境搭建Maven依赖深度解析
Spring Boot测试的核心依赖在pom.xml中配置如下
dependencies!-- 核心测试库包含JUnit 5、Mockito、AssertJ等 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency!-- MockMvc独立测试支持 --dependencygroupIdorg.springframework/groupIdartifactIdspring-test/artifactIdscopetest/scope/dependency!-- AssertJ流式断言库 --dependencygroupIdorg.assertj/groupIdartifactIdassertj-core/artifactIdversion3.24.2/versionscopetest/scope/dependency
/dependencies关键依赖详解
spring-boot-starter-test测试核心包包含 JUnit JupiterJUnit 5测试引擎Mockito模拟依赖对象JSONPathJSON数据解析AssertJ流式断言 spring-test提供MockMvc等Spring测试工具类assertj-core增强版断言库支持链式调用 为什么选择JUnit 5而不是JUnit 4 JUnit 5的模块化架构JupiterPlatformVintage支持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/DELETEandExpect()验证响应结果状态码、Header、BodyjsonPath()使用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; // 模拟ServiceTestvoid 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; // 真实ServiceMockBeanprivate ProductRepository repo; // 模拟RepositoryTestvoid 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集合断言
ListProduct products service.search(手机);assertThat(products).hasSize(3).extracting(Product::getPrice).allMatch(price - price 1000);六、测试架构最佳实践
1. 分层测试金字塔 #mermaid-svg-XOksp3XyFHeA24pU {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-XOksp3XyFHeA24pU .error-icon{fill:#552222;}#mermaid-svg-XOksp3XyFHeA24pU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XOksp3XyFHeA24pU .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-XOksp3XyFHeA24pU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XOksp3XyFHeA24pU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XOksp3XyFHeA24pU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XOksp3XyFHeA24pU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XOksp3XyFHeA24pU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XOksp3XyFHeA24pU .marker.cross{stroke:#333333;}#mermaid-svg-XOksp3XyFHeA24pU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XOksp3XyFHeA24pU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XOksp3XyFHeA24pU .cluster-label text{fill:#333;}#mermaid-svg-XOksp3XyFHeA24pU .cluster-label span{color:#333;}#mermaid-svg-XOksp3XyFHeA24pU .label text,#mermaid-svg-XOksp3XyFHeA24pU span{fill:#333;color:#333;}#mermaid-svg-XOksp3XyFHeA24pU .node rect,#mermaid-svg-XOksp3XyFHeA24pU .node circle,#mermaid-svg-XOksp3XyFHeA24pU .node ellipse,#mermaid-svg-XOksp3XyFHeA24pU .node polygon,#mermaid-svg-XOksp3XyFHeA24pU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XOksp3XyFHeA24pU .node .label{text-align:center;}#mermaid-svg-XOksp3XyFHeA24pU .node.clickable{cursor:pointer;}#mermaid-svg-XOksp3XyFHeA24pU .arrowheadPath{fill:#333333;}#mermaid-svg-XOksp3XyFHeA24pU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XOksp3XyFHeA24pU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XOksp3XyFHeA24pU .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-XOksp3XyFHeA24pU .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-XOksp3XyFHeA24pU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XOksp3XyFHeA24pU .cluster text{fill:#333;}#mermaid-svg-XOksp3XyFHeA24pU .cluster span{color:#333;}#mermaid-svg-XOksp3XyFHeA24pU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XOksp3XyFHeA24pU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} UI Tests Controller Tests Service Tests Repository Tests 底层测试Service/Repository数量最多顶层测试Controller覆盖关键接口
2. 测试数据管理策略
方式适用场景示例内存数据库Repository层测试H2 DataJpaTestSql初始化固定数据场景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断言可构建高效的测试体系。关键实践
使用WebMvcTest MockMvc测试Controller不启动Web服务器Service层用SpringBootTest MockBean进行集成测试利用JSONPath高效验证复杂JSON响应通过Sql管理测试数据生命周期 避坑指南避免在Controller测试中加载完整上下文SpringBootTest否则会导致测试速度下降10倍以上 实战项目源码下载 | Spring官方测试指南