为什么需要写Java单元测试总结
目录
前言
一、为什么写单元测试
写单测好处
1、提升效率
2、场景覆盖全
单测怎么写
1、集成测试
2、单元测试
Mock框架
1、Mockito单元测试
2、Mockito 中文文档地址
二、强制要求
1.好的单元测试必须遵守AIR原则。
2.单元测试应该是全自动执行的,并且非交互式的。
3.保持单元测试的独立性。
4.单元测试是可以重复执行的,不能受到外界环境的影响。
被测系统
5.对于单元测试,要保证测试粒度足够小。
6.核心业务、核心应用、核心模块的增量代码,确保单元测试通过。
7.单元测试代码目录
三、推荐要求
1.单元测试的基本目标
2.编写单元测试代码时遵守BCDE原则,以保证被测试模块的交付质量。
3.对于数据库的相关查询、更新和删除等操作
4.和数据库相关的单元测试
5.对于不可测的代码
6.在设计评审阶段
7.单元测试作为一种质量保障手段
四、参考要求
1.为了更方便地进行单元测试,业务代码应该避免一下情况
2.不要对单元测试存在如下误解
五、单元测试与集成测试的区别
1.测试对象不同
2.测试方法不同
3.测试时间不同
4.测试内容不同
六、为什么要使用Mock做测试
1.Mock可以用来解除外部服务依赖,从而保证了测试用例的独立性。
2.Mock可以减少全链路测试数据准备,从而提高了编写测试用例的速度。
3.Mock可以模拟一些非正常的流程,从而保证了测试用例的代码覆盖率。
4.Mock可以不用加载项目环境配置,从而保证了测试用例的执行速度。
前言
为了保证代码质量,在写完代码后,写单测是很有必要的。当然,在大部分情况下,我们可能不会写单测,而是直接把应用部署起来,直接自测,然后再联调。估计很大一部分人,都是用这种方式开发。当然,我之前也是按这个方式来开发,单测覆盖率纯粹是为了满足公司的指标要求,大部分流于形式。
一、为什么写单元测试
说到单元测试,就不得不提起另一个词,TDD(Test-Driven Development)测试驱动开发:在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码
测试驱动开发虽然饱受争议,不过有这种方法论的推出并有不少的同行在践行,起码能够说明测试的重要性。
1、当我们想测试部分代码逻辑是否正常的时候,我们可能会直接psvm
来构造数据进而调试。那如果有一种东西能把我们psvm
统一放到某个地方呢?
2、当我们在一个系统里边修改了很多代码时,又不确定改动是否影响在核心逻辑时。那如果有一种东西能在编译的时候,顺便自动跑一遍逻辑做回归呢?无论是重构还是正式提测前,都提高了自己写代码的信心。
3、当我们很容易一不小心时就把代码写成一坨屎,那如果有一种东西能让我们在编码的时候就注重自己的代码设计呢?
4、当我们这个季度什么都没干,但是系统没发生过故障,那如果有一种东西能让我们在KPI上添上浓墨的一笔呢?
5、....欢迎补充
没错,这东西就是单元测试。
写单测好处
慢慢的,我感受到写单测带来的几个好处:
1、提升效率
启动一个应用,几分钟,找bug,修bug,再重启应用,这个过程不断的重复,应用重启太浪费时间。
而单测不需要重启整个应用,只对几个service做测试,效率高很多。
2、场景覆盖全
单测可以对代码运行中的各种情况进行模拟,并对最终的返回结果断言,这是自己自测很难模拟的。而且,这些单测,是沉淀的资产。下次修改代码,可以重跑以前单测,发现问题,避免踩坑。
单测怎么写
我们很容易对单测产生误解,所以这里我先把2个概念说明一下:
1、集成测试
测试过程中,会启动整个Spring容器,调用DB 或者 依赖的外部接口等。只不过访问的环境是测试环境。这个过程最大程度还原生产环境过程,但是耗时长。
2、单元测试
不启动整个应用,只对单个接口/类进行测试。不调用DB 、外部接口,依赖的服务都Mock掉,只测试代码逻辑。这个过程,测试用例耗时短。
我们说的单测,是指第2种。
单测过程分2步,第一步:Mock外部依赖,第二步:断言
Mock框架
1、Mockito单元测试
<dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>3.3.3</version><scope>test</scope></dependency>
2、Mockito 中文文档地址
Mockito库能够Mock对象、验证结果以及打桩(stubbing)。
GitHub - hehonghui/mockito-doc-zh: Mockito框架中文文档
二、强制要求
1.好的单元测试必须遵守AIR原则。
-
A:Automatic(自动化)
-
I:Independent(独立性)
-
R:Repeatable(可重复)
单元测试在线上运行时,感觉像空气(AIR)一样感觉不到,但在测试质量的保障上,却是非常关键的。好的单元测试宏观上来说,具有自动化、独立性、可重复执行的特点。
2.单元测试应该是全自动执行的,并且非交互式的。
测试用例通常是被定期执行的,执行过程必须完全自动化才有意义。输出结果需要人工检查的测试,不是一个好的单元测试。
单元测试中不准使用System.out来进行人肉认证,必须使用assert来验证。
3.保持单元测试的独立性。
为了保证单元测试稳定可靠且便于维护,单元测试用例之间绝不能互相调用,也不能依赖执行的先后次序。
4.单元测试是可以重复执行的,不能受到外界环境的影响。
单元测试通常会被放到持续集成中,每次有代码check in时单元测试都会被执行。
如果单测对外部环境(网络、服务、中间件等)有依赖,容易导致持续继承机制的不可用。
正例:为了不受外界环境影响,要求设计代码时就把 SUT 的依赖改成注入,在测试时用 spring 这样的 DI框架注入一个本地(内存)实现或者 Mock 实现。
被测系统
被测系统(System under test, SUT)表示正在被测试的系统,目的是测试系统能否正确操作。
根据测试类型的不同, SUT 指代的内容也不同, 例如 SUT 可以是一个类甚至是一整个系统。
5.对于单元测试,要保证测试粒度足够小。
有助于精确定位问题,单测粒度至多是类级别,一般是方法级别。
6.核心业务、核心应用、核心模块的增量代码,确保单元测试通过。
说明:新增代码及时补充单元测试,如果新增代码影响了原有单元测试,请及时修正。
7.单元测试代码目录
必须写在如下工程目录:src/test/java,不允许写在业务代码目录下。
说明:源码编译时会跳过此目录,而单元测试框架默认是扫描此目录。
三、推荐要求
1.单元测试的基本目标
语句覆盖率达到70%,核心模块的语句覆盖率和分支覆盖率都要达到100%。
说明:在DAO层,Manager层和可重用度高的Service中都应该进行单元测试。
分支覆盖率 :5个分支,那么对应的应该有10条语句(一个分支有两条语句,ture和false),如果你执行了其中的5条,那么覆盖率就是50%。
2.编写单元测试代码时遵守BCDE原则,以保证被测试模块的交付质量。
-
B:Border,边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等。
-
C:Correct,正确的输入,并得到预期的结果。
-
D:Design,与设计文档相结合,来编写单元测试。
-
E:Error,强制错误信息输入,并得到预期的结果。
3.对于数据库的相关查询、更新和删除等操作
不能假设数据库里的数据是存在的,或者直接操作数据库将数据插进去,请使用程序插入或者导入数据的方式来准备数据。
4.和数据库相关的单元测试
可以设定自动回滚机制,不给数据库造成脏数据,或者对单元测试产生的数据有明确的前后缀标识。
正例:在企业智能事业部的内部单元测试中,使用 ENTERPRISE_INTELLIGENCE UNIT_TEST 的前缀来标识单元测试相关代码。
5.对于不可测的代码
在适当的时机做必要的重构,使代码变得可测,避免为了达到测试要求而书写不规范测试代码。
6.在设计评审阶段
开发人员需要和测试人员一起确定单元测试范围,单元测试最好覆盖所有测试用例。
7.单元测试作为一种质量保障手段
在项目提测前完成单元测试,不建议项目发布后补充单元测试用例。
四、参考要求
1.为了更方便地进行单元测试,业务代码应该避免一下情况
-
构造方法中做的事情过多
-
存在过多的全局变量和静态方法
-
存在过多的外部依赖
-
存在过多的条件语句
说明:多层条件语句建议使用卫语句、策略模式、状态模式等方式重构。
如果条件语句极其复杂,就应该将条件语句拆解开,然后逐个检查,并在条件为真时立刻从函数中返回,这样的单独检查通常被称之为“卫语句”(guard clauses)
摘自《重构---改善既有代码的设计》
卫语句的效果就是将原来需要仔细阅读代码、细心整理逻辑的条件判断整理成一眼能看透的逻辑关系,效果就像以下:
if(obj != null){doSomething();}
转换成卫语句以后的代码如下:
if(obj == null){return;}doSomething();
2.不要对单元测试存在如下误解
-
那是测试同学干的事情。单元测试也是和开发同学强相关的。
-
单元测试代码是多余的。系统的整体功能和各单元部件的测试真长与否是强相关的。
-
单元测试代码不需要维护。一年半载后单元测试几乎都会处于废弃朱状态。
-
单元测试与线上故障没有辩证关系。好的单元测试能够最大限度地规避线上故障。
五、单元测试与集成测试的区别
在实际工作中,不少同学用集成测试代替了单元测试,或者认为集成测试就是单元测试。
单元测试与集成测试的区别:
1.测试对象不同
单元测试对象是实现了具体功能的程序单元,集成测试对象是概要设计规划中的模块及模块间的组合。
2.测试方法不同
单元测试中的主要方法是基于代码的白盒测试,集成测试中主要使用基于功能的黑盒测试。
3.测试时间不同
集成测试要晚于单元测试。
4.测试内容不同
单元测试主要是模块内程序的逻辑、功能、参数传递、变量引用、出错处理及需求和设计中具体要求方面的测试;而集成测试主要验证各个接口、接口之间的数据传递关系,及模块组合后能否达到预期效果。
六、为什么要使用Mock做测试
比如你现在想要测试一个方法是否是正常的,但是这个方法中有很多调用数据库的代码,那么我们就可以在每个调用数据库的地方打桩,模拟一下访问完数据库之后的返回值,这样我们就可以在测试的时候避免访问数据库了,可以非常高效地完成我们的单元测试,已达到验证我们写的方法到底对不对的目的。
1.Mock可以用来解除外部服务依赖,从而保证了测试用例的独立性。
现在的互联网软件系统,通常采用了分布式部署的微服务,为了单元测试某一服务而准备其它服务,存在极大的依耐性和不可行性。
2.Mock可以减少全链路测试数据准备,从而提高了编写测试用例的速度。
传统的集成测试,需要准备全链路的测试数据,可能某些环节并不是你所熟悉的。最后,耗费了大量的时间和经历,并不一定得到你想要的结果。现在的单元测试,只需要模拟上游的输入数据,并验证给下游的输出数据,编写测试用例并进行测试的速度可以提高很多倍。
3.Mock可以模拟一些非正常的流程,从而保证了测试用例的代码覆盖率。
根据单元测试的BCDE原则,需要进行边界值测试(Border)和强制错误信息输入(Error),这样有助于覆盖整个代码逻辑。在实际系统中,很难去构造这些边界值,也能难去触发这些错误信息。而Mock从根本上解决了这个问题:想要什么样的边界值,只需要进行Mock;想要什么样的错误信息,也只需要进行Mock。
4.Mock可以不用加载项目环境配置,从而保证了测试用例的执行速度。
在进行集成测试时,我们需要加载项目的所有环境配置,启动项目依赖的所有服务接口。往往执行一个测试用例,需要几分钟乃至几十分钟。采用Mock实现的测试用例,不用加载项目环境配置,也不依赖其它服务接口,执行速度往往在几秒之内,大大地提高了单元测试的执行速度。
相关文章:

为什么需要写Java单元测试总结
目录 前言 一、为什么写单元测试 写单测好处 1、提升效率 2、场景覆盖全 单测怎么写 1、集成测试 2、单元测试 Mock框架 1、Mockito单元测试 2、Mockito 中文文档地址 二、强制要求 1.好的单元测试必须遵守AIR原则。 2.单元测试应该是全自动执行的,并…...

Gin框架: 控制器, 中间件的分层设计案例
对控制器的分组与继承 1 )设计项目目录结构 yourGinProject/ 根目录├── go.mod go mod 文件├── go.sum go sum 文件├── main.go main 文件└── tpls html模板目录│ └── web│ │ └── index.html├── routers 路由目录│ …...

日常遇到Maven出现依赖版本/缓存问题通用思路。
Maven依赖错误联想 明明自己的工程是直接从大佬哪里拉下来的,并且自己的setting文件也是没有问题,可是自己偏偏编译有问题。这里介绍一种通用解决方案,仅供参考。 前置排查确认 我遇到原因是在JDK升级过程中遇到的: java.lang.…...

安卓11-HDMI插拔检测流程
hdmi从插入到拔出经过底层一系列检测到应用层,应用层获取hdmi插入状态后又会做出一系列相应的动作,下面梳理了从应用层到底层一步步追踪到芯片的hpd-pin的检测过程。 frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.…...

OkHttp Retrofit HttpClient之间的区别
OkHttp、Retrofit 和 HttpClient 是三个不同的 HTTP 客户端库,它们各自有不同的特点和用途。下面是它们之间的主要区别: 1. **OkHttp**: - OkHttp 是一个高性能的 HTTP 和 HTTP/2 客户端,由 Square 公司开发。 - 它…...

Paddlepaddle使用自己的VOC数据集训练目标检测(0废话简易教程)
一 安装paddlepaddle和paddledection(略) 笔者使用的是自己的数据集 二 在dataset目录下新建自己的数据集文件,如下: 其中 xml文件内容如下: 另外新建一个createList.py文件: # -- coding: UTF-8 -- imp…...

【解析】C语言两个实例
例一: 下面程序输出什么? int main() { int i 43; int n printf("%d\n",i); printf("%d\n",n); return 0; } 大家深入考虑一下为什么返回是3这背后有什么鲜为人知的秘密到底是C语言离奇的规定还是深思熟…...

阅读笔记(Multimedia Systems2020)Review on image-stitching techniques
Wang Z, Yang Z. Review on image-stitching techniques[J]. Multimedia Systems, 2020, 26: 413-430. DOI https://doi.org/10.1007/s00530-020-00651-y...

【Java程序员面试专栏 数据结构】三 高频面试算法题:栈和队列
一轮的算法训练完成后,对相关的题目有了一个初步理解了,接下来进行专题训练,以下这些题目就是汇总的高频题目,因为栈和队列这两哥们结构特性比较向对应,所以放到一篇Blog中集中练习 题目题干直接给出对应博客链接,这里只给出简单思路、代码实现、复杂度分析 题目关键字…...

Python | Conda常用命令
一、介绍 1、Anaconda工具 Anaconda是一个用于数据科学和机器学习的开源软件包管理器和环境管理器。它包含了许多流行的数据科学工具和库,如Python、Jupyter Notebook、numpy、pandas、scikit-learn等,可以帮助用户轻松地管理和安装这些工具和库。Anaco…...

Linux 驱动开发基础知识——APP 怎么读取按键值(十二)
个人名片: 🦁作者简介:学生 🐯个人主页:妄北y 🐧个人QQ:2061314755 🐻个人邮箱:2061314755qq.com 🦉个人WeChat:Vir2021GKBS 🐼本文由…...

【FastAPI】P3 请求与响应
目录 请求路径参数查询参数 响应JSON 响应文本响应返回 Pydantic 模型 在网络通讯中,请求(Request) 与 响应(Response) 扮演着至关重要的角色,它们构成了客户端与服务器间互动的根本理念。 请求࿰…...

Python学习-流程图、分支与循环(branch and loop)
十、流程图 1、流程图(Flowchart) 流程图是一种用于表示算法或代码流程的框图组合,它以不同类型的框框代表不同种类的程序步骤,每两个步骤之间以箭头连接起来。 好处: 1)代码的指导文档 2)有助…...

Python Flask Web 框架学习笔记+完整项目
Flask是一个轻量级的基于Python的web框架。 我们建议使用最新版本的 Python。Flask 支持 Python 3.8 及更高版本。 官网:欢迎使用 Flask — Flask 文档 (3.0.x) (palletsprojects.com) RESTFul API:Python Flask高级编程之REST…...

XML Map 端口进阶篇——常用关键字和格式化器详解
XML Map 端口是用于在不同XML之间建立关系映射的工具,允许通过拖拽操作实现源XML和目标 XML之间的数据字段映射,除此之外,XML Map 端口还提供了其它丰富多彩的功能,使用户能够更加灵活和高效的处理XML 数据映射任务,让…...

排序算法之——直接插入排序
直接插入排序——以升序排列为例 1.1基本思想1.2动态图示感知1.3静态图示详解1.4代码实现1.5时间复杂度1.5.1最好情况1.5.2最差情况 1.6空间复杂度1.7稳定性1.7.1一个小问题 1.1基本思想 把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直…...

突出最强算法模型——回归算法 !!
文章目录 1、特征工程的重要性 2、缺失值和异常值的处理 (1)处理缺失值 (2)处理异常值 3、回归模型的诊断 (1)残差分析 (2)检查回归假设 (3)Cooks 距离 4、学…...

云数据库 Redis 性能深度评测(阿里云、华为云、腾讯云、百度智能云)
在当今的云服务市场中,阿里云、腾讯云、华为云和百度智能云都是领先的云服务提供商,他们都提供了全套的云数据库服务,其中 Redis属于RDS 之后第二被广泛应用的服务,本次测试旨在深入比较这四家云服务巨头在Redis云数据库性能方面的…...

Android---Retrofit实现网络请求:Java 版
简介 在 Android 开发中,网络请求是一个极为关键的部分。Retrofit 作为一个强大的网络请求库,能够简化开发流程,提供高效的网络请求能力。 Retrofit 是一个建立在 OkHttp 基础之上的网络请求库,能够将我们定义的 Java 接口转化为…...

使用静态CRLSP配置MPLS TE隧道
正文共:1591 字 13 图,预估阅读时间:4 分钟 静态CRLSP(Constraint-based Routed Label Switched Paths,基于约束路由的LSP)是指在报文经过的每一跳设备上(包括Ingress、Transit和Egress…...

gentoo安装笔记
最近比较闲,所以挑战一下自己,在自己的台式电脑上安装gentoo 下面记录了我亲自安装的步骤,作为以后我再次安装时参考所用。 整体步骤 一般来将一个linux发行版的安装步骤其实大体上都差不多,基本分为一下几步: 1. …...

Git如何使用 五分钟快速入门
Git如何使用 五分钟快速入门 Git是一个分布式版本控制系统,它可以帮助开发人员跟踪和管理项目的代码变更。与传统的集中式版本控制系统(如SVN)不同,Git允许开发人员在本地存储完整的代码仓库,并且可以独立地进行代码修…...

FreeRTOS学习笔记——(FreeRTOS临界段代码保护及调度器挂起与恢复)
这里写目录标题 1,临界段代码保护简介(熟悉)2,临界段代码保护函数介绍(掌握)3,任务调度器的挂起和恢复(熟悉) 1,临界段代码保护简介(熟悉…...

箱形理论在交易策略中的实战应用与优化
箱形理论,简单来说,就是将价格波动分成一段一段的方框,研究这些方框的高点和低点,来推测价格的趋势。 在上升行情中,价格每突破新高价后,由于群众惧高心理,可能会回跌一段,然后再上升…...

MinIO 和 Apache Tika:文本提取模式
Tl;dr: 在这篇文章中,我们将使用 MinIO Bucket Notifications 和 Apache Tika 进行文档文本提取,这是大型语言模型训练和检索增强生成 LLM和RAG 等关键下游任务的核心。 前提 假设我想构建一个文本数据集,然后我可以用它来微调 LLM.为了做…...

c编译器学习05:与chibicc类似的minilisp编译器(待续)
minilisp项目介绍 项目地址:https://github.com/rui314/minilisp 作者也是rui314,commits也是按照模块开发提交的。 minilisp只有一个代码文件:https://github.com/rui314/minilisp/blob/master/minilisp.c 加注释也只有996行。 代码结构&a…...

手撕qsort函数
前言 本篇主要讲解的是qsort函数细节以及运用实例。 紧跟我的脚步一起手撕qsort函数吧~ 欢迎关注个人主页:逸狼 更多优质内容: 拿捏c语言指针(上) 拿捏c语言指针(中) 拿捏c语言指针(下&…...

项目在linux上的简单部署
本文章只介绍项目的简单部署,暂时没有Docker部署。 项目部署有两种方式,一种是直接命令部署,第二种是用脚本,脚本本身也是将命令进行封装来执行。 命令 项目通过maven打包,启动命令: # 启动命令 nohup …...

MySQL安装教程(详细版)
今天分享的是Win10系统下MySQL的安装教程,打开MySQL官网,按步骤走呀~ 宝们安装MySQL后,需要简单回顾一下关系型数据库的介绍与历史(History of DataBase) 和 常见关系型数据库产品介绍 呀,后面就会进入正式…...

Linux platform tree下的单总线驱动程序设计(DHT11)
目录 概述 1 认识DHT11 1.1 DHT11特性 1.2 DHT11数据格式 1.3 DHT11与MCU通信 1.4 DHT11信号解析 1.4.1 起始信号 1.4.2 解析信号0 1.4.3 解析信号1 2 驱动开发 2.1 硬件接口 2.2 更新设备树 2.2.1 添加驱动节点 2.2.2 编译.dts 2.2.3 更新板卡中的.dtb 2.3 驱…...