怎么将网站权重提上去,网站开发代做,百度贴吧官网app下载,服务器连接wordpressSpring Boot 3 中 JUnit 5 使用详解
我们从「能用」到「用好」逐步拆解 Spring Boot 3 中 JUnit 5 的使用#xff0c;全程结合实际开发场景#xff0c;所有代码可直接运行。
基础认知#xff1a;为什么要在 Spring Boot 中用 JUnit#xff1f;
实际开发中#xff0c;我们写…Spring Boot 3 中 JUnit 5 使用详解我们从「能用」到「用好」逐步拆解 Spring Boot 3 中 JUnit 5 的使用全程结合实际开发场景所有代码可直接运行。基础认知为什么要在 Spring Boot 中用 JUnit实际开发中我们写的 Controller、Service、工具类都需要验证逻辑是否正确——比如用户注册时的参数校验、订单计算的金额是否准确。手动测试比如启动项目调接口效率低而 JUnit 能让我们写「自动化测试用例」代码写完就能验证还能在打包、部署前自动执行避免低级错误。Spring Boot 3 内置了 JUnit 5替代了老版本的 JUnit 4核心依赖是spring-boot-starter-test无需额外配置就能用。第一步环境准备1. 创建 Spring Boot 3 项目用 Spring Initializr 创建项目选择Spring Boot 3.2依赖Spring Web、Spring Boot Starter Test自动包含 JUnit 5、AssertJ、Mockito 等2. 核心依赖pom.xml 关键部分dependencies!-- Spring Boot 测试核心依赖 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency!-- Web 依赖用于 Controller 测试 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency/dependencies第二步入门案例——测试简单工具类无 Spring 依赖先从「最基础的纯 Java 方法测试」入手不依赖 Spring 容器理解 JUnit 5 的核心注解。场景测试金额计算工具类实际开发中订单系统常需要计算折扣后金额我们先写工具类再写测试用例。1. 待测试的工具类// src/main/java/com/example/demo/util/PriceCalculator.javapackagecom.example.demo.util;/** * 金额计算工具类 */publicclassPriceCalculator{/** * 计算折扣后金额 * param originalPrice 原价 * param discountRate 折扣率0.8 表示 8 折 * return 折扣后金额保留 2 位小数 */publicstaticdoublecalculateDiscountPrice(doubleoriginalPrice,doublediscountRate){// 边界校验原价和折扣率不能为负if(originalPrice0||discountRate0){thrownewIllegalArgumentException(原价和折扣率不能为负数);}// 计算并保留 2 位小数doubleresultoriginalPrice*discountRate;returnMath.round(result*100)/100.0;}}2. JUnit 5 测试用例测试类放在src/test/java下包结构和主类一致// src/test/java/com/example/demo/util/PriceCalculatorTest.javapackagecom.example.demo.util;importorg.junit.jupiter.api.Test;importstaticorg.junit.jupiter.api.Assertions.*;/** * 金额计算工具类测试 */// JUnit 5 无需类级注解直接写测试方法publicclassPriceCalculatorTest{// 测试正常场景100 元打 8 折预期 80.0TestvoidtestCalculateDiscountPrice_Normal(){doubleresultPriceCalculator.calculateDiscountPrice(100,0.8);// 断言实际结果等于预期结果允许 0.001 误差assertEquals(80.0,result,0.001);}// 测试边界场景原价为 0TestvoidtestCalculateDiscountPrice_ZeroPrice(){doubleresultPriceCalculator.calculateDiscountPrice(0,0.9);assertEquals(0.0,result);}// 测试异常场景折扣率为负预期抛出 IllegalArgumentExceptionTestvoidtestCalculateDiscountPrice_NegativeDiscount(){// 断言方法会抛出指定异常IllegalArgumentExceptionexceptionassertThrows(IllegalArgumentException.class,()-PriceCalculator.calculateDiscountPrice(100,-0.5));// 验证异常信息assertEquals(原价和折扣率不能为负数,exception.getMessage());}}运行测试在 IDEA 中右键点击测试类 → RunPriceCalculatorTest控制台会显示测试结果绿色对勾表示通过红色叉号表示失败核心知识点入门级注解/方法作用Test标记测试方法JUnit 会自动执行assertEquals断言实际值等于预期值支持数值、字符串、对象等assertThrows断言方法执行时会抛出指定类型的异常assertTrue/assertFalse断言布尔值为 true/false第三步进阶案例——测试 Spring BeanService 层实际开发中Service 层依赖 Repository、其他 Service需要启动 Spring 容器才能测试。Spring Boot 提供了SpringBootTest注解自动加载上下文。场景测试用户服务UserService用户服务包含「根据 ID 查询用户」「新增用户」逻辑依赖模拟的 Repository。1. 实体类// src/main/java/com/example/demo/entity/User.javapackagecom.example.demo.entity;publicclassUser{privateLongid;privateStringname;privateIntegerage;// 构造器、getter/setter、toStringpublicUser(){}publicUser(Longid,Stringname,Integerage){this.idid;this.namename;this.ageage;}// getter/setter 省略实际开发中用 Lombok 的 Data 更方便publicLonggetId(){returnid;}publicvoidsetId(Longid){this.idid;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.namename;}publicIntegergetAge(){returnage;}publicvoidsetAge(Integerage){this.ageage;}OverridepublicStringtoString(){returnUser{idid, namename\, ageage};}}2. Repository 层模拟// src/main/java/com/example/demo/repository/UserRepository.javapackagecom.example.demo.repository;importcom.example.demo.entity.User;importorg.springframework.stereotype.Repository;importjava.util.HashMap;importjava.util.Map;importjava.util.Optional;RepositorypublicclassUserRepository{// 模拟数据库privatestaticfinalMapLong,UserUSER_DBnewHashMap();static{// 初始化测试数据USER_DB.put(1L,newUser(1L,张三,20));USER_DB.put(2L,newUser(2L,李四,25));}// 根据 ID 查询用户publicOptionalUserfindById(Longid){returnOptional.ofNullable(USER_DB.get(id));}// 新增用户publicUsersave(Useruser){LongnewIdUSER_DB.keySet().stream().max(Long::compare).orElse(0L)1;user.setId(newId);USER_DB.put(newId,user);returnuser;}}3. Service 层// src/main/java/com/example/demo/service/UserService.javapackagecom.example.demo.service;importcom.example.demo.entity.User;importcom.example.demo.repository.UserRepository;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importjava.util.Optional;ServicepublicclassUserService{AutowiredprivateUserRepositoryuserRepository;/** * 根据 ID 查询用户 * param id 用户 ID * return 用户信息若不存在则抛出异常 */publicUsergetUserById(Longid){returnuserRepository.findById(id).orElseThrow(()-newRuntimeException(用户不存在IDid));}/** * 新增用户年龄校验必须大于 0 * param user 用户信息 * return 新增后的用户带 ID */publicUsercreateUser(Useruser){if(user.getAge()null||user.getAge()0){thrownewIllegalArgumentException(年龄必须大于 0);}returnuserRepository.save(user);}}4. Service 层测试用例// src/test/java/com/example/demo/service/UserServiceTest.javapackagecom.example.demo.service;importcom.example.demo.entity.User;importcom.example.demo.repository.UserRepository;importorg.junit.jupiter.api.Test;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importstaticorg.junit.jupiter.api.Assertions.*;/** * UserService 测试启动 Spring 容器 */// 启动 Spring Boot 上下文自动扫描 BeanSpringBootTestpublicclassUserServiceTest{// 自动注入 Spring 容器中的 UserServiceAutowiredprivateUserServiceuserService;// 自动注入 Repository可选用于验证数据AutowiredprivateUserRepositoryuserRepository;// 测试正常查询用户TestvoidtestGetUserById_Success(){UseruseruserService.getUserById(1L);// 断言用户信息正确assertEquals(张三,user.getName());assertEquals(20,user.getAge());}// 测试查询不存在的用户预期抛异常TestvoidtestGetUserById_NotFound(){RuntimeExceptionexceptionassertThrows(RuntimeException.class,()-userService.getUserById(999L));assertEquals(用户不存在ID999,exception.getMessage());}// 测试新增用户正常场景TestvoidtestCreateUser_Success(){// 准备测试数据UsernewUsernewUser();newUser.setName(王五);newUser.setAge(30);// 执行新增方法UsersavedUseruserService.createUser(newUser);// 断言结果assertNotNull(savedUser.getId());// ID 不为空assertEquals(王五,savedUser.getName());assertEquals(30,savedUser.getAge());// 验证 Repository 中确实存在该用户UserfoundUseruserRepository.findById(savedUser.getId()).orElse(null);assertNotNull(foundUser);}// 测试新增用户年龄为负预期抛异常TestvoidtestCreateUser_InvalidAge(){UserinvalidUsernewUser();invalidUser.setName(赵六);invalidUser.setAge(-5);IllegalArgumentExceptionexceptionassertThrows(IllegalArgumentException.class,()-userService.createUser(invalidUser));assertEquals(年龄必须大于 0,exception.getMessage());}}核心知识点进阶级注解/特性作用SpringBootTest启动 Spring Boot 上下文加载所有 Bean模拟真实运行环境Autowired在测试类中注入 Spring 容器中的 BeanassertNotNull断言对象不为 null常用语验证返回的实体、ID 等测试隔离性每次测试方法执行后Spring 上下文默认复用但数据会重置保证测试独立第四步高级案例——测试 Controller 层模拟 HTTP 请求实际开发中Controller 层接收 HTTP 请求返回响应需要模拟接口调用。Spring Boot 提供了WebMvcTest注解专门测试 Controller无需启动完整 Spring 上下文效率更高。场景测试用户接口UserController1. Controller 层// src/main/java/com/example/demo/controller/UserController.javapackagecom.example.demo.controller;importcom.example.demo.entity.User;importcom.example.demo.service.UserService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.http.HttpStatus;importorg.springframework.http.ResponseEntity;importorg.springframework.web.bind.annotation.*;RestControllerRequestMapping(/api/users)publicclassUserController{AutowiredprivateUserServiceuserService;/** * 根据 ID 查询用户 * param id 用户 ID * return 用户信息 */GetMapping(/{id})publicResponseEntityUsergetUserById(PathVariableLongid){UseruseruserService.getUserById(id);returnResponseEntity.ok(user);}/** * 新增用户 * param user 用户信息 * return 新增后的用户 */PostMappingpublicResponseEntityUsercreateUser(RequestBodyUseruser){UsersavedUseruserService.createUser(user);returnResponseEntity.status(HttpStatus.CREATED).body(savedUser);}}2. Controller 层测试用例// src/test/java/com/example/demo/controller/UserControllerTest.javapackagecom.example.demo.controller;importcom.example.demo.entity.User;importcom.example.demo.service.UserService;importcom.fasterxml.jackson.databind.ObjectMapper;importorg.junit.jupiter.api.Test;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;importorg.springframework.boot.test.mock.mockito.MockBean;importorg.springframework.http.MediaType;importorg.springframework.test.web.servlet.MockMvc;importstaticorg.mockito.ArgumentMatchers.any;importstaticorg.mockito.Mockito.when;importstaticorg.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;importstaticorg.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;importstaticorg.springframework.test.web.servlet.result.MockMvcResultMatchers.*;/** * UserController 测试仅启动 Web 层模拟 HTTP 请求 */// 仅加载 Web 相关 BeanController、HandlerMapping 等不加载 Service/RepositoryWebMvcTest(UserController.class)publicclassUserControllerTest{// 模拟 HTTP 请求的核心工具AutowiredprivateMockMvcmockMvc;// 序列化/反序列化 JSON用于请求体转换AutowiredprivateObjectMapperobjectMapper;// 模拟 UserService避免依赖真实 Service解耦测试MockBeanprivateUserServiceuserService;// 测试查询用户接口成功场景TestvoidtestGetUserById_Success()throwsException{// 1. 模拟 Service 返回数据UsermockUsernewUser(1L,张三,20);when(userService.getUserById(1L)).thenReturn(mockUser);// 2. 模拟 GET 请求并验证响应mockMvc.perform(get(/api/users/1)// 请求路径.contentType(MediaType.APPLICATION_JSON))// 请求类型.andExpect(status().isOk())// 响应状态码 200.andExpect(jsonPath($.id).value(1))// 响应 JSON 的 id 字段为 1.andExpect(jsonPath($.name).value(张三))// name 字段为 张三.andExpect(jsonPath($.age).value(20));// age 字段为 20}// 测试查询用户接口失败场景TestvoidtestGetUserById_NotFound()throwsException{// 1. 模拟 Service 抛异常when(userService.getUserById(999L)).thenThrow(newRuntimeException(用户不存在ID999));// 2. 模拟 GET 请求验证响应mockMvc.perform(get(/api/users/999).contentType(MediaType.APPLICATION_JSON)).andExpect(status().is5xxServerError())// 响应状态码 500.andExpect(content().string(containsString(用户不存在ID999)));// 响应内容包含异常信息}// 测试新增用户接口成功场景TestvoidtestCreateUser_Success()throwsException{// 1. 准备测试数据UserrequestUsernewUser();requestUser.setName(王五);requestUser.setAge(30);UserresponseUsernewUser(3L,王五,30);// 2. 模拟 Service 返回数据when(userService.createUser(any(User.class))).thenReturn(responseUser);// 3. 模拟 POST 请求验证响应mockMvc.perform(post(/api/users).contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(requestUser)))// 请求体转 JSON.andExpect(status().isCreated())// 响应状态码 201.andExpect(jsonPath($.id).value(3)).andExpect(jsonPath($.name).value(王五));}}核心知识点高级注解/工具作用WebMvcTest仅加载 Web 层 Bean专注测试 Controller启动速度比SpringBootTest快MockMvc模拟 HTTP 请求GET/POST/PUT/DELETE无需启动服务器MockBean模拟 Service/Repository解耦测试不依赖真实实现jsonPath解析响应 JSON验证字段值如$.name表示 JSON 中的 name 字段ObjectMapper将 Java 对象转为 JSON 字符串用于构造请求体第五步实战技巧贴近真实开发1. 测试命名规范测试方法名要清晰一眼看出「测试场景 预期结果」比如testGetUserById_Success查询用户-成功testCreateUser_InvalidAge新增用户-年龄无效2. 测试分层策略层级测试注解核心目标工具类无纯 JUnit验证逻辑正确性ServiceSpringBootTest验证业务逻辑、依赖调用ControllerWebMvcTest验证请求映射、参数解析、响应3. 跳过测试个别测试暂时不想运行用Disabled注解TestDisabled(暂时跳过待修复 XXX 问题)voidtestTempSkip(){// ...}4. 测试生命周期注解作用BeforeEach每个测试方法执行前执行比如初始化测试数据AfterEach每个测试方法执行后执行比如清理数据BeforeAll所有测试方法执行前执行一次静态方法AfterAll所有测试方法执行后执行一次静态方法示例BeforeEachvoidsetUp(){// 每个测试方法执行前初始化数据System.out.println(开始执行测试方法...);}总结Spring Boot 3 中 JUnit 5 的使用遵循「由浅入深」的逻辑纯 Java 方法直接用 JUnit 核心断言无需 SpringSpring Bean用SpringBootTest启动容器注入 Bean 测试Web 层用WebMvcTestMockMvc模拟 HTTP 请求解耦测试。实际开发中写测试用例不是「额外工作」而是「提效手段」——能提前发现 bug减少手动测试成本尤其是在迭代升级时修改代码后跑一遍测试就能快速验证是否影响原有功能。所有代码均可直接复制到 Spring Boot 3 项目中运行建议先跑通基础案例再逐步尝试 Service 和 Controller 层测试加深理解。