当前位置: 首页 > news >正文

【单元测试】SpringBoot

【单元测试】SpringBoot

1. 为什么单元测试很重要?‼️

在这里插入图片描述

从前,有一个名叫小明的程序员,他非常聪明,但有一个致命的缺点:懒惰。小明的代码写得又快又好,但他总觉得单元测试是一件麻烦事,觉得代码能跑就行,测试什么的全是浪费时间。

有一天,小明接到了一个重要的项目,他需要为一个在线购物网站开发一个新功能:用户可以在结账时使用优惠券。小明想:“这还不简单?半小时搞定!”于是,他迅速写好了代码,迫不及待地提交了。

第二天,项目经理来了,满脸怒气地对小明说:“小明,你的代码出问题了!所有用户在使用优惠券时都得到了负数的折扣,他们的账户反而被扣了更多的钱!”

小明惊讶地张大了嘴巴,不敢相信自己会犯这么低级的错误。他连忙检查代码,发现确实在计算折扣时,忘记处理负数的情况。小明赶紧修复了这个错误,但心里还是觉得不服气:“这只是个小问题,我不需要写单元测试。”

几天后,小明又收到一个新任务:实现一个积分系统,用户每消费一元就能积一分。小明想:“这次我一定不会犯错。”于是,他又快速写好了代码,提交了上去。

然而,不久之后,客户打电话过来抱怨:“我的积分怎么越消费越少了?!”

小明再次检查代码,发现自己在积分计算的函数里不小心多写了一个减号,导致积分被扣除而不是增加。他这次终于意识到,如果自己早点写单元测试,这些问题完全可以在开发阶段就被发现,而不是在上线后被用户发现。

于是,小明决定改过自新,认真学习单元测试。他发现,单元测试不仅可以帮助他捕捉到代码中的错误,还能让他更加自信地进行代码重构和优化。

总结
  1. 🔍早期发现问题:单元测试能够在开发阶段及时发现代码中的错误,避免错误在后期被发现,减少修复成本。

  2. 🐛确保代码质量:通过编写单元测试,可以验证每个模块的功能是否按预期工作,提升代码的可靠性和稳定性。

  3. 🔨方便重构:在进行代码重构或优化时,有单元测试作为保障,可以放心地修改代码,而不必担心引入新的错误

  4. 📄文档作用:单元测试可以作为代码的活文档,帮助新成员快速理解代码的功能和使用方法

2. 快速入门

2.1 基础配置

在 pom.xml 中添加以下依赖:

<dependencies><!-- Spring Boot Starter Test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies>

这个依赖包含了多个库和功能,主要有以下几个:

  • JUnit:JUnit是Java中最流行和最常用的单元测试框架,它提供了一套注解和断言来编写和运行单元测试。例如@Test注解表示一个测试方法,assertEquals断言表示两个值是否相等。
  • Spring Test:Spring Test是一个基于Spring的测试框架,它提供了一套注解和工具来配置和管理Spring上下文和Bean。例如@SpringBootTest注解表示一个集成测试类,@Autowired注解表示自动注入一个Bean。
  • Mockito:Mockito是一个Java中最流行和最强大的Mock对象库,它可以模拟复杂的真实对象行为,从而简化测试过程。例如@MockBean注解表示创建一个Mock对象,when方法表示定义Mock对象的行为。
  • Hamcrest:Hamcrest是一个Java中的匹配器库,它提供了一套语义丰富而易读的匹配器来进行结果验证。例如assertThat断言表示验证一个值是否满足一个匹配器,is匹配器表示两个值是否相等。
  • AssertJ:AssertJ是一个Java中的断言库,它提供了一套流畅而直观的断言语法来进行结果验证。例如assertThat断言表示验证一个值是否满足一个条件,isEqualTo断言表示两个值是否相等。

除了以上这些库外,spring-boot-starter-test还包含了其他一些库和功能,如JsonPath、JsonAssert、XmlUnit等。这些库和功能可以根据不同的测试场景进行选择和使用。

Mockito详解地址:https://pdai.tech/md/develop/ut/dev-ut-x-mockito.html

2.2 编写单元测试

为了更好的演示如何编写单元测试,以最简单的用户登录为例🌰

项目结构

src
├── main
│   └── java
│       └── com
│           └── hwq
│               └── fuwork01
│                   ├── common
│                   ├── controller
│                   │   └── UserController.java
│                   ├── dto
│                   ├── exception
│                   └── service
│                   		└── UserService
└── test└── java└── com└── hwq└── fuwork01├── controller│   └── UserControllerTest.java└── service└──FuWork01ApplicationTests.java
UserServiceImpl(Service层)
/**
* @author wqh
* @description 针对表【user(用户表)】的数据库操作Service实现
* @createDate 2024-07-15 17:13:27
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>implements UserService{@Overridepublic Long userLogin(String userAccount, String userPassword, HttpServletRequest request) {LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();userLambdaQueryWrapper.eq(User::getUserAccount, userAccount).eq(User::getUserPassword, userPassword);User user = this.getOne(userLambdaQueryWrapper);if (user == null) {throw new BusinessException(ErrorCode.NOT_FOUND_ERROR ,"用户不存在");}// 存储用户登录态request.getSession().setAttribute("userLogin", user);return user.getId();}@Overridepublic User getLoginUser(HttpServletRequest request) {return (User)request.getSession().getAttribute("userLogin");}}
针对Service层的测试
@SpringBootTest
public class UserServiceTest {@Resourceprivate UserService userService;private HttpServletRequest request;@BeforeEachvoid setUp() {// 模拟构造requestrequest = new MockHttpServletRequest();}/*** 测试用户登录*/@Testvoid userLogin() {String userAccount = "huang";String userPassword = "huangwenqing";Long userId = userService.userLogin(userAccount, userPassword, request);// 验证结果对象与user对象相等assertThat(userId, Matchers.is(1L));}}

解释

  • request = new MockHttpServletRequest(),构造一个模拟的request
  • assertThat,判断userId是否符合正常
新断言assertThat使用

JUnit 4.4 结合 Hamcrest 提供了一个全新的断言语法——assertThat。程序员可以只使用 assertThat 一个断言语句,结合 Hamcrest 提供的匹配符,就可以表达全部的测试思想。

assertThat 的优点:

优点 1: 以前 JUnit 提供了很多的 assertion 语句,如:assertEquals,assertNotSame,assertFalse,assertTrue,assertNotNull,assertNull 等,现在有了 JUnit 4.4,一条 assertThat 即可以替代所有的 assertion 语句,这样可以在所有的单元测试中只使用一个断言方法,使得编写测试用例变得简单,代码风格变得统一,测试代码也更容易维护。

优点 2: assertThat 使用了 Hamcrest 的 Matcher 匹配符,用户可以使用匹配符规定的匹配准则精确的指定一些想设定满足的条件,具有很强的易读性,而且使用起来更加灵活。

优点 3: assertThat 不再像 assertEquals 那样,使用比较难懂的“谓宾主”语法模式(如:assertEquals(3, x);),相反,assertThat 使用了类似于“主谓宾”的易读语法模式(如:assertThat(x,is(3));),使得代码更加直观、易读。

UserController(登录控制层)
@RestController
@RequestMapping("/user")
@CrossOrigin("*")
public class UserController {@Resourceprivate UserService userService;@PostMapping("/login")public BaseResponse<Long> userLogin(@RequestBody UserLoginDTO userLoginDTO, HttpServletRequest request) {if (userLoginDTO == null) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数错误");}String userAccount = userLoginDTO.getUserAccount();String userPassword = userLoginDTO.getUserPassword();if (StringUtils.isEmpty(userAccount)) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "账户不得为空");}if (StringUtils.isEmpty(userPassword)) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码不得为空");}return ResultUtils.success(userService.userLogin(userAccount, userPassword, request));}
}

内容

  • 对上传的登录参数进行校验
  • 登录成功,返回用户id
针对controller层单元测试
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerTest {@Autowiredprivate MockMvc mockMvc;@MockBeanprivate UserService userService;/*** 用户登录成功测试* @throws Exception*/@Testvoid testUserLoginSuccess() throws Exception {UserLoginDTO userLoginDTO = new UserLoginDTO();userLoginDTO.setUserAccount("huang");userLoginDTO.setUserPassword("huangwenqing");// userService测试when(userService.userLogin("huang", "huangwenqing", new MockHttpServletRequest())).thenReturn(1L);// 模拟http登录请求mockMvc.perform(post("/user/login").contentType(MediaType.APPLICATION_JSON).content("{\"userAccount\":\"huang\",\"userPassword\":\"huangwenqing\"}")).andExpect(status().isOk()).andExpect(jsonPath("$.code").value(0)).andExpect(jsonPath("$.data").value(0L));}/*** 用户登录异常测试* @throws Exception*/@Testvoid testUserLoginNullParams() throws Exception {mockMvc.perform(post("/user/login").contentType(MediaType.APPLICATION_JSON).content("{}")).andExpect(status().isOk()).andExpect(jsonPath("$.code").value(40000));}
}

要点

  • @SpringBootTest: 加载完整的 Spring 应用程序上下文。
  • @AutoConfigureMockMvc: 自动配置 MockMvc,用于模拟 HTTP 请求。
  • MockMvc: 用于模拟 HTTP 请求和响应的测试。
  • @MockBean: 创建并注入一个模拟的 UserService 实例,以便于测试控制器而不需要实际的服务实现。
  • 使用 mockMvc.perform 方法发送 POST 请求,模拟用户登录。
  • 使用 andExpect 方法验证 HTTP 状态码和响应体中的数据。
2.3测试原则
  1. 保持测试独立:确保每个测试独立运行,不依赖其他测试的执行结果
  2. 使用模拟对象:对于外部依赖,如数据库、网络请求等,尽量使用模拟对象,以提高测试的速度和稳定性。
  3. 覆盖各种场景:编写充分的测试用例,覆盖正常路径、异常路径和边界条件。
  4. 保持测试简洁:测试代码应该简洁明了,避免过于复杂的逻辑,以提高可维护性。

3.总结

编写优雅的单元测试是保证代码质量的关键。在 Spring Boot 中,我们可以使用 @SpringBootTest 和 @AutoConfigureMockMvc 等注解简化测试配置,使用 Mockito 等工具模拟依赖,编写覆盖全面的测试用例。通过遵循最佳实践,我们可以编写高效、稳定的单元测试,提高开发效率和代码质量。

希望本文对您在 Spring Boot 项目中编写单元测试有所帮助。Happy Testing!

相关文章:

【单元测试】SpringBoot

【单元测试】SpringBoot 1. 为什么单元测试很重要&#xff1f;‼️ 从前&#xff0c;有一个名叫小明的程序员&#xff0c;他非常聪明&#xff0c;但有一个致命的缺点&#xff1a;懒惰。小明的代码写得又快又好&#xff0c;但他总觉得单元测试是一件麻烦事&#xff0c;觉得代码…...

分布式搜索引擎ES-elasticsearch入门

1.分布式搜索引擎&#xff1a;luceneVS Solr VS Elasticsearch 什么是分布式搜索引擎 搜索引擎&#xff1a;数据源&#xff1a;数据库或者爬虫资源 分布式存储与搜索&#xff1a;多个节点组成的服务&#xff0c;提高扩展性(扩展成集群) 使用搜索引擎为搜索提供服务。可以从海量…...

TCP三次握手与四次挥手详解

1.什么是TCP TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的通信协议&#xff0c;属于互联网协议族&#xff08;TCP/IP&#xff09;的一部分。TCP 提供可靠的、顺序的、无差错的数据传输服务&…...

【Windows】操作系统之任务管理器(第一篇)

一、操作系统简介 Windows操作系统是由微软公司&#xff08;Microsoft&#xff09;开发的一款图形操作系统&#xff0c;它以其强大的功能和广泛的用户基础&#xff0c;成为了目前世界上用户使用最多、兼容性最强的操作系统之一。以下是关于Windows操作系统的详细介绍&#xff…...

图同构的必要条件

来源&#xff1a;离散数学...

Django获取request请求中的参数

支持 post put json_str request.body # 属性获取最原始的请求体数据 json_dict json.loads(json_str)# 将原始数据转成字典格式 json_dict.get("key", "默认值") # 获取数据参考 https://blog.csdn.net/user_san/article/details/109654028...

kotlin compose 实现应用内多语言切换(不重新打开App)

1. 示例图 2.具体实现 如何实现上述示例,且不需要重新打开App ①自定义 MainApplication 实现 Application ,定义两个变量: class MainApplication : Application() { object GlobalDpData { var language: String = "" var defaultLanguage: Strin…...

记录些MySQL题集(16)

MySQL 存储过程与触发器 一、初识MySQL的存储过程 Stored Procedure存储过程是数据库系统中一个十分重要的功能&#xff0c;使用存储过程可以大幅度缩短大SQL的响应时间&#xff0c;同时也可以提高数据库编程的灵活性。 存储过程是一组为了完成特定功能的SQL语句集合&#x…...

【算法基础】Dijkstra 算法

定义&#xff1a; g [ i ] [ j ] g[i][j] g[i][j] 表示 v i v_i vi​ 到 $v_j $的边权重&#xff0c;如果没有连接&#xff0c;则 g [ i ] [ j ] ∞ g[i][j] \infty g[i][j]∞ d i s [ i ] dis[i] dis[i] 表示 v k v_k vk​ 到节点 v i v_i vi​ 的最短长度&#xff0c; …...

使用 Flask 3 搭建问答平台(三):注册页面模板渲染

前言 前端文件下载 链接https://pan.baidu.com/s/1Ju5hhhhy5pcUMM7VS3S5YA?pwd6666%C2%A0 知识点 1. 在路由中渲染前端页面 2. 使用 JinJa 2 模板实现前端代码复用 一、auth.py from flask import render_templatebp.route(/register, methods[GET]) def register():re…...

pycharm如何debug for循环里面的错误值

一般debug时&#xff0c;在for循环里面的话&#xff0c;需要自己一步一步点。如果循环几百次那种就比较麻烦。此时可以采用try except的方式来解决 例子如下 #ptyhon debug for循环的代码 num[1,2,3,s,4] ans0 for i in num:try:ansiexcept:print(错误) print(ans) 结果如下&a…...

解决网页中的 video 标签在移动端浏览器(如百度访问网页)视频脱离文档流播放问题

问题现象 部分浏览器视频脱离文档流&#xff0c;滚动时&#xff0c;视频是悬浮出来&#xff0c;在顶部播放 解决方案 添加下列属性&#xff0c;可解决大部分浏览器的脱离文档流的问题 <videowebkit-playsinline""playsInlinex5-playsinlinet7-video-player-t…...

.Net--CLS,CTS,CLI,BCL,FCL

1.什么是CLS&#xff1f; 所以.NET专门为此参考每种语言(例如C# &#xff0c;VB&#xff0c;F#)并找出了语言间的共性&#xff0c;然后定义了一组规则&#xff0c;开发者都遵守这个规则来编码&#xff0c;那么代码就能被任意.NET平台支持的语言所通用。 而与其说是规则&#x…...

Stable Diffusion:质量高画风清新细节丰富的二次元大模型二次元插图

今天和大家分享一个基于Pony模型训练的二次元模型&#xff1a;二次元插图。关于该模型有4个不同的分支版本。 1.5版本&#xff1a;loar模型&#xff0c;推荐底模型niji-动漫二次元4.5。 xl版本&#xff1a;SDXL模型版本 mix版本&#xff1a;光影减弱&#xff0c;减少SDXL版本…...

数读MEME之争:以太坊获更高价值共识,抢占热点成Solana流量密码

在当前显著的加密牛市中&#xff0c;以太坊和Solana之间的竞争不仅在币价表现上显而易见&#xff0c;生态发展方面也备受关注。特别是在这轮MEME行情中&#xff0c;双方阵营的MEME代币呈现出不同的特点和趋势。 市场表现对比 以太坊的优势&#xff1a; 市场份额和认可度更高&…...

python的with语句

1.with语句的作用 在 Python 中&#xff0c;with 语句用于创建一个上下文管理器&#xff0c;以更简洁和安全的方式管理资源。 其主要优点是可以确保在代码块执行完毕后&#xff0c;相关资源能够被正确释放或清理&#xff0c;即使在代码块内部发生了异常。 以下是一个使用 with…...

Selenium原理深度解析

在自动化测试领域&#xff0c;Selenium无疑是最受欢迎和广泛使用的工具之一。它支持多种浏览器和操作系统&#xff0c;为开发人员和测试人员提供了强大的自动化测试解决方案。本文将深入探讨Selenium的工作原理&#xff0c;包括其架构、核心组件、执行流程以及它在自动化测试中…...

算法复杂度<数据结构 C版>

什么是算法复杂度&#xff1f; 简单来说算法复杂度是用来衡量一个算法的优劣的&#xff0c;一个程序在运行时&#xff0c;对运行时间和运行空间有要求&#xff0c;即时间复杂度和空间复杂度。 目录 什么是算法复杂度&#xff1f; 大O的渐近表达式 时间复杂度示例 空间复杂度…...

【XSS】

文章目录 0x01 简介0x02 XSS Payload用法XSS攻击平台及调试JavaScript 0x03 XSS绕过XSS漏洞防御策略 跨站脚本攻击&#xff0c;Cross Site Script。&#xff08;重点在于脚本script&#xff09; 有关XSS可以造成的 危害&#xff0c;见 0x02 XSS Payload用法 分类 反射型、存储…...

Go网络编程-RPC程序设计

gRPC 通信 RPC 介绍 RPC, Remote Procedure Call&#xff0c;远程过程调用。与 HTTP 一致&#xff0c;也是应用层协议。该协议的目标是实现&#xff1a;调用远程过程&#xff08;方法、函数&#xff09;就如调用本地方法一致。 如图所示&#xff1a; 说明&#xff1a; Servi…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站&#xff1a;https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本&#xff1a; Windows版&#xff08;推荐下载标准版&#xff09; Windows系统安装步骤 运行安装程序&#xff1a; 双击下载的.exe安装文件 如果出现安全提示&…...

怎么让Comfyui导出的图像不包含工作流信息,

为了数据安全&#xff0c;让Comfyui导出的图像不包含工作流信息&#xff0c;导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo&#xff08;推荐&#xff09;​​ 在 save_images 方法中&#xff0c;​​删除或注释掉所有与 metadata …...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用

在工业制造领域&#xff0c;无损检测&#xff08;NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统&#xff0c;以非接触式光学麦克风技术为核心&#xff0c;打破传统检测瓶颈&#xff0c;为半导体、航空航天、汽车制造等行业提供了高灵敏…...

QT开发技术【ffmpeg + QAudioOutput】音乐播放器

一、 介绍 使用ffmpeg 4.2.2 在数字化浪潮席卷全球的当下&#xff0c;音视频内容犹如璀璨繁星&#xff0c;点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频&#xff0c;到在线课堂中知识渊博的专家授课&#xff0c;再到影视平台上扣人心弦的高清大片&#xff0c;音…...

云原生安全实战:API网关Envoy的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关 作为微服务架构的统一入口&#xff0c;负责路由转发、安全控制、流量管理等核心功能。 2. Envoy 由Lyft开源的高性能云原生…...

职坐标物联网全栈开发全流程解析

物联网全栈开发涵盖从物理设备到上层应用的完整技术链路&#xff0c;其核心流程可归纳为四大模块&#xff1a;感知层数据采集、网络层协议交互、平台层资源管理及应用层功能实现。每个模块的技术选型与实现方式直接影响系统性能与扩展性&#xff0c;例如传感器选型需平衡精度与…...

性能优化中,多面体模型基本原理

1&#xff09;多面体编译技术是一种基于多面体模型的程序分析和优化技术&#xff0c;它将程序 中的语句实例、访问关系、依赖关系和调度等信息映射到多维空间中的几何对 象&#xff0c;通过对这些几何对象进行几何操作和线性代数计算来进行程序的分析和优 化。 其中&#xff0…...

LeetCode - 148. 排序链表

目录 题目 思路 基本情况检查 复杂度分析 执行示例 读者可能出的错误 正确的写法 题目 148. 排序链表 - 力扣&#xff08;LeetCode&#xff09; 思路 链表归并排序采用"分治"的策略&#xff0c;主要分为三个步骤&#xff1a; 分割&#xff1a;将链表从中间…...

【Docker 02】Docker 安装

&#x1f308; 一、各版本的平台支持情况 ⭐ 1. Server 版本 Server 版本的 Docker 就只有个命令行&#xff0c;没有界面。 Platformx86_64 / amd64arm64 / aarch64arm(32 - bit)s390xCentOs√√Debian√√√Fedora√√Raspbian√RHEL√SLES√Ubuntu√√√√Binaries√√√ …...