《码出高效:java开发手册》八 -单元测试

news2024/12/29 10:51:33

前言

这章内容主要是讲单测,单元就是指一个程序分的最小单位,一般是类或者方法,在面向对象编程里,一般就是认为方法是最小单位,单测是程序功能的基本保障,在软件上线前非常重要的一环

正文

单测的好处:
1、提升软件质量:单元测试可以保障开发质量和程序的鲁棒性。开发时不断地测试能帮助发现程序的问题,进而定位和排查。越早发现问题,修复的成本也会越低;三种情况从好到坏:1、提前发现问题 2、出问题快速定位 3、跟在问题后疲于奔命,功能回归
2、 促进代码优化:在测试的过程中,工程师会优化代码的执行效率,时间空间复杂度降低就会让程序更好
3、提升研发效率:编写单测虽然会增加程序开发的时间,但后续排错和维护会大大节约时间,提升整体研发效率
4、增加重构自信:重构是非常困难的,修改一个底层结构就会影响大量上层服务,单测通过就可以提升重构的自信和勇气
单测可以说是测试人员的工作,但对开发人员来说,也是一种基本素养的体现

单元测试的基本原则

单元测试宏观要符合AIR原则,微观符合BCDE原则
新增代码应该同步增加单测用例,修改代码逻辑也要保证单测都能通过
AIR具体原则包括:
A:Automatic自动化:测试用例必须是自动化的,不允许使用System.out验证,必须使用断言验证
I:Independent独立性:独立性就是不允许用例间相互调用,否则导致运行效率降低,或者错误导致程序中断
R:Repetable可重复:主流测试框架中, JUnit 的用例执行顺序是无序的,而TestNG 支持测试用例的顺序执行,单测是可重复执行,不受外部环境影响,有提交代码就会触发单测执行
编写单测要保证粒度足够小,最好到方法级,跨类跨系统的逻辑不负责,这个属于集成测试的范围,需要保证BCDE原则:
B: Border : 边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等。
C: Correct : 正确的输入, 并得到预期的结果。
D: Design :与设计文档相结合,来编写单元测试。
E : Error :单元测试的目标是证明程序有错,而不是程序无错。为了发现代码中潜在的错误, 我们需要在编写测试用例时有一些强制的错误输入(如非法数据、异常流程、非业务允许输入等)来得到预期的错误结果。

  • 由于单测只是运行前的模拟测试,一些因素不具备,可以Mock:
    功能因素。比如被测试方法内部调用的功能不可用。
    时间因素。比如双十一还没有到来,与此时间相关的功能点。
    环境因素。政策环境,如支付宝政策类新功能,多端环境, 如PC 、手机等。
    数据因素。线下数据样本过小,难以覆盖各种线上真实场景。
    其他因素。为了简化测试编写, 开发者也可以将一些复杂的依赖采用Mock方式实现。
    可以使用硬编码实现mock,也可以使用配置文件,也可以使用一些mock框架,例如JMockit 、EasyMock 、JMock 等,使用mock可以隔离一些复杂因素,写出稳定的单测,得到稳定的结果

单元测试覆盖率

单测覆盖是指业务代码被测试的程度,从粗到细,从弱到强如下:
1、粗粒度
通常只要执行到类或方法里的代码,即使只有部分,也是覆盖了这个类或方法,通常这个覆盖率可以达到100%,但也不够
2、细粒度
(1)行覆盖
行就是代码的行,覆盖的行/代码总行数就是行覆盖率
在这里插入图片描述
如图有三个入参,5句可执行语句,编写如下测试用例
在这里插入图片描述

执行结果

在这里插入图片描述
上例中覆盖率100%,但c==3并没有执行到,并且a!=1 并且c!=3也没有执行到,可见覆盖率不代表完整程度,但由于好计算,行测也很常用
(2)分支测试
分支覆盖也叫判定覆盖,就是对各种可能情况进行覆盖判定,与下面的容颜混淆
(3)条件判定覆盖
条件判定覆盖要求所有可能条件都执行一次,并且可能结果也至少执行一次
在这里插入图片描述
在这里插入图片描述

这里的csvsource就是入参两种,023和103,分别为true和false,覆盖了可能结果
并且第二个000入参算进去,把三个条件的真假都覆盖到了
(4)条件组合覆盖
这个就是把所有条件的排列组合都来一遍,是条件判定覆盖的一个子集
在这里插入图片描述
在这里插入图片描述
这组有8组入参,就是字面意义上的3个布尔元素排组合,2的三次方,8种,如果是n个条件,就是2的n次方个,可见工作量很大
(5)路径覆盖
路径覆盖要求能够测试到程序中所有可能的路径。在testMethod 方法中,可能的路径有① a= 1, b=2 ② a1b!=2,c3 ③ a=1 ,b!=2 ,c! =3 ④ a! =1,c=3 ⑤ a !=1,c!=3
这5种。当存在“||”时, 如果第一个条件已经为true ,不再计算后边表达式的值。
而当存在“&& ” 时,如果第一个条件已经为false ,同样不再计算后边表达式的值。
这里少了3个,因为去除了判定
在这里插入图片描述
在这里插入图片描述

单元测试编写

这节介绍如何编写单测
单测框架用的多的有JUnit 和TestNG
这里用Junit5,可能有不兼容Junit4
Junit5有三个模块组成:
JUnit Platform· 用于在NM 上启动测试框架, 统一命令行、Gradle 和Maven、等方式执行测试的人口。
JUnit Jupiter: 包含JUnit5.x 全新的编程模型和扩展机制。
JUnit Vintage : 用于在新的框架中兼容运行JUnit3.x 和JUnit4.x 的测试用例。
Juint有很多注解,如下图
在这里插入图片描述
下图是一个测试类的结构
在这里插入图片描述
在这里插入图片描述
需要注意的是,@DisplayName 注解仅仅对于采用IDE 或图形化方式展示测试运行结果的场景有效

在这里插入图片描述
对于使用Maven 或Gradle 等命令行方式运行单元测试的情况,该注解中的内
容会被忽略;例如单元测试出错时,实际展示结果如下·
在这里插入图片描述
当测试用例较多时,为了更好地组织测试的结构,推荐使用JUnit 的@Nested 注解来表达有层次关系的测试用例:
在这里插入图片描述
Junit没有嵌套限制,但不建议超过三层,否则很难理解
分组测试有一些技巧,比如分为不同强度:“执行很快且很重要”的冒烟测试用例、“执行很慢但同样比较重要”的曰常测试用例,以及“ 数量
很多但不太重要”的回归测试用例。
使用Junit的@Tag注解可以区分强度
在这里插入图片描述
通过标签选择执行的用例类型, 在Maven 中可以通过配置maven-surefire-pIugin插件来实现·
在这里插入图片描述
这里运行fast而不运行slow

在Gradle 中可以通过JUnit 专用的junitPlatform 配置来实现
在这里插入图片描述
在这里插入图片描述
一些算法逻辑,计算逻辑很多,需要数据驱动测试,如果按照平常的逻辑,需要写很多用例,使用@TestFactory注解,可以将数据输入输出与测试逻辑分开,一次编写就可对各种结果进行验证
在这里插入图片描述

这里的数字意思是传入12小时,20分种,24秒,0毫秒

命名

通常测试类为被测类加Test后缀,如DemoService 的测试类应该命名为DemoServiceTest

在这里插入图片描述

如上为目录,单测代码不能再业务目录下,主流Java 测试框架如JUnit, TestNG 测试代码都是默认放在src/test/java 下的,
测试资源文件则放在src/test/resources 下,
规范单测名称有利于提高测试质量
在这里插入图片描述
如上,实例二明显更加清楚,可以猜测到时用户令牌出了问题
主流单测有两种:加test前后缀或者should…when…,比如shouldSuccess WhenDecodeUserToken
同时,避免过长名字

断言和假设

定义好测试方法后,再细化就是断言和假设了,断言是封装了一些常用逻辑,不满足则判定失败;假设类似,但它不满足,测试就直接退出,记录状态为跳过
常用的断言被封装在org.junit.jupiter.api.Assertions中,均为静态方法,如下表是一些常用断言方法
在这里插入图片描述
在这里插入图片描述
假设提供的静态方法更加简单,被封装在org.junit.jupiter.api.Assumptions 类中, 同样为静态方法
在这里插入图片描述
断言的选择,要求更精准,比如asse此Equals( 100, result) 语句优于assertTrue(100 == result) 语句。
如果没有合适的断言,也可以自己定义构造条件,然后不符合条件就用fail断言标记为失败
一般断言两个参数,第一个是预期,第二个是实际结果
在这里插入图片描述
预期加10,实际减10,运行结果
在这里插入图片描述
assertTimeout 和assertTimeoutPreemptively断言区别在于:前者结束后会继续执行记录实际操作时间,后者会直接结束
在这里插入图片描述
实际开发中,如果JUnit的断言不能满足需求,可以使用AssertJ
它的特点是流式断言,一个对象可以连接多个判断,进行多次断言

对于自定义JavaBean对象
AssertJ 通过AssertJ assertions generator 来生成对应的XxxAssert 类,然后辅助我们对模板JavaBean 对象进行断言API 判断。AssertJ
assertions generator 有相应的Maven 和Gradle Plugin , 生成这样的代码非常容易, 所以很容易实现对自定义JavaBean 对象的判断需求。它还有一些拓展,DB assertions, Guava assertions等,可以对Mybatis和Hibernate进行单测
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如上为Junit的断言,如果用AssertJ就是这样
在这里插入图片描述
可见不同判断连接到了一起,流式断言充分利用了Java 8 之后的匿名方法和Stream 类型的特点,很好地对JUnit 断言方法进行了补充。

后记

还有一章是代码规约,这一章其实是另外一本书的缩略,之后有空会把那本书也弄过来

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

_Linux多线程-死锁Linux线程同步篇

文章目录1. 死锁死锁四个必要条件避免死锁避免死锁算法(了解)2. Linux线程同步线程同步出现的背景条件变量同步概念与竞态条件条件变量函数1. 初始化2. 销毁3. 等待条件满足4. 唤醒等待小结测试实验1. 死锁 死锁是指在一组进程中的各个进程均占有不会释…

linux服务器CPU飙高排查

文章目录前言一、第一步 top二、根据pid查找具体线程2.根据pid找到16进制3. 根据进程和线程查找原因总结前言 系统cpu飙高,尤其对于后端人员来说,其实应该学会排查,这样也算是综合能力的体现;那么当出现了cpu严重飙高的时候怎么排查呢? 一、第一步 top 直接在问题服务器输入…

如何系统地学习 C++ 语言?

C 最大的缺点就是语法复杂,而且难学难精。 学习 C 语言也许一本 K&R 的《C程序设计语言》就够了,但是在 C 世界里,找不到这样一本书。在这个爱速成的年代,大家可能耐不住寂寞花很长时间去学习一门语言,所以可以看…

车载以太网 - DoIP电源模式 - 07

DoIP电源模式&节点状态&激活检查这3块内容没有太大的逻辑性可言,主要是概念性内容介绍,所以本篇内容可能会略显枯燥,不过我回尽量完整的把这几块内容介绍清晰,为后面的测试用例设计做好铺垫,方便大家在看完文章后,更加快速的提取知识点,并完成测试设计。 诊断电…

回溯法--图的m着色问题--子集树

问题描述 给定无向连通图和m种不同的颜色,用这些颜色为图G的各个顶点着色,每个顶点有一种颜色 是否有一种着色方法?使得图G中每条边的两个顶点有不同的颜色 这个问题就是图的m可着色判定问题 色数:如果有一个图最少需要m种颜色…

C进阶:自定义类型:结构体、枚举、联合体

自定义类型:结构体、枚举、联合体自定义类型:结构体、枚举、联合体结构体结构的定义:结构体的声明:特殊的声明(匿名)结构体的自引用:结构体变量的定义和初始化结构体内存对齐为什么要有内存对齐修改默认对齐数结构体传…

李宏毅ML-自动调整学习速率

自动调整学习速率 文章目录自动调整学习速率1. RMS and Adagrad2. RMSProp and Adam3. Learning rate scheduling3. 总结1. RMS and Adagrad 在下面有两幅图,如第一幅图所示,随着 iteration 的增加,loss 在不断减少,最后趋近于 0…

广告业务系统 之 智能保险丝 —— “智能流控”

文章目录广告业务系统 之 智能保险丝 —— “智能流控”智能流控常规流量调控数据源计算智能流控功能挂载阈值存储架构长短板服务构建及部署广告业务系统 之 智能保险丝 —— “智能流控” 除了 在 AB 环节 设计了出色的 重试机制 —— “ 双发 ” 外,在 ADX 系统的…

【Redis】Redis实现全局唯一ID

【Redis】Redis实现全局唯一ID 为什么要使用Redis实现全局唯一ID去替代传统的数据库自增ID,主要原因如下: 数据库自增ID的规律性太明显受单表数据量的限制,数据量很大时分表会出现ID重复的现象 1. 全局ID生成器 出于以上原因,我…

Vue3——第十三章(插槽 Slots)

一、插槽内容与出口 这里有一个 <FancyButton> 组件&#xff0c;可以像这样使用&#xff1a; 而 <FancyButton> 的模板是这样的&#xff1a; <slot> 元素是一个插槽出口 (slot outlet)&#xff0c;标示了父元素提供的插槽内容 (slot content) 将在哪里被…

excel图表技巧:如何用填充单元格制作比率分析图

在工作中&#xff0c;我们经常要向上级领导汇报某个指标的进度或完成情况。有时候&#xff0c;我们会用仪表盘或温度计图来展示数据。不会这类型图表的朋友&#xff0c;不用担心&#xff0c;因为今天&#xff0c;我将教给大家一种更简单的方法&#xff01;公司新来的职员小明&a…

你是真的“C”——详解C语言函数模块知识(下篇)

详解C语言函数模块知识(下篇&#xff09;&#x1f60e;前言&#x1f64c;1、 函数的嵌套调用和链式访问&#x1f64c;1.1 嵌套调用&#x1f49e;1.2 链式访问&#x1f49e;2、函数的声明和定义&#x1f64c;2.1函数声明&#x1f49e;2.1函数定义&#x1f49e;3、函数递归&#…

【算法题解】9. 邻值查找

文章目录题目解题思路代码实现复杂度分析这是一道中等难度的题。 题目来自&#xff1a;AcWing 题目 给定一个长度为 n 的序列 A&#xff0c;A 中的数各不相同。 对于 A 中的每一个数 Ai&#xff0c;求&#xff1a; min|Ai−Aj|&#xff0c;其中 1 < j < i。 以及令上式…

10行代码带你轻松抓取博客清单

一、前言 今天在网上偶遇一款html解析利器HtmlAgilityPack&#xff0c;免费下载地址&#xff1a;入口。 HtmlAgilityPack是.net下的一个HTML解析类库&#xff0c;支持用XPath来解析HTML。通过该类库&#xff0c;先通过浏览器获取到xpath获取到节点内容然后再通过正则表达式匹…

【operator bool】while(cin >> str)是什么意思?

文章目录一、前言二、cin是什么&#xff1f;三、隐式类型转化如何发生&#xff1f;一、前言 在oj题中&#xff0c;为了实现多行输入&#xff0c;我们经常可以看到这样的写法&#xff1a;while(cin >> str)&#xff0c;这究竟是什么意思呢&#xff1f;为了理解其中的含义&…

c++ 可变参数的三种实现方式

c 可变参数 方法一&#xff1a; C语言的: va_list1 #include <stdio.h> #include <stdarg.h>int add_nums(int count, ...) {int result 0;va_list args;va_start(args, count); // C23 起能省略 countfor (int i 0; i < count; i) {result va_arg(args, i…

bresenham algorithm

#! https://zhuanlan.zhihu.com/p/598780689 bresenham algorithm 全象限区域bresenham algorithm计算的python/c实现 bresenham algorithm为计算机图形学中使用像素点显示直线的算法&#xff0c;算法使用整数运算&#xff0c;能大幅提升计算速度。最近概率栅格建图算法中涉及…

CloudCanal实战-Oracle数据迁移同步到PostgreSQL

简述 本篇文章主要介绍如何使用 CloudCanal 构建一条 Oracle 到 PostgreSQL 的数据同步链路 技术要点 缩小的数据库权限要求 CloudCanal 对 Oracle 数据库的高权限要求&#xff0c;主要来自两个面向 DBA 的操作&#xff0c;自动构建字典和 自动切换归档日志&#xff0c;这两…

详解 strtok 函数以及模拟实现

目录 一、strtok 函数的介绍 二、strtok 函数的模拟实现 一、strtok 函数的介绍 函数原型&#xff1a; char* strtok(char* str, const char* delimiters); delimiter n.[计]分隔符&#xff0c;定界符&#xff08;a character that marks the beginning or end of a unit o…

KMP算法详解+动图演示

目录 一、KMP算法简介 二、KMP算法的详细图解 1. 先了解BF算法的基本思路 2. 简单了解KMP算法 3. next数组的引入 4. next数组的代码实现&#xff08;含动态演示&#xff09; 三、KMP算法完整代码 一、KMP算法简介 KMP算法是一种改进的字符串匹配算法&#xff0c;由 …