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

软件测试技术之单元测试—工程师 Style 的测试方法(3)

如何设计单元测试?

单元测试设计方法

单元测试用例,和普通测试用例的设计,没有太多不同,常见的就是等价类划分、边界值分析等。而测试用例的设计其实也是开发者应该掌握的基本技能。

等价类划分

把所有输入划分为若干分类,从每个分类中选取少数有代表性的数据做为测试用例。

例如,一个方法计算输入参数的绝对值的倒数,如果是输入是 0,则抛异常。那么对这个方法写测试的话,就应该有三个等价类,输入是负数、0 以及正数。所以我可以选取一个负数、一个正数以及 0 来设计三个测试用例。

再举个例子,某个方法是根据医生的认证状态,发送不同的消息。那么等价类可能有三种,未认证、普通认证但无权威认证、普通认证且权威认证,某些情况下可能还会包括无普通认证但有威认证。

边界值分析

边界值是指划分等价类后,在边界附近的一些输入数据,这些输入往往是最容易出错的。

例如,对于上面计算绝对值的倒数的例子,那么边界值就包括 Integer.min、-1、0、1、Integer.max 等。再举个例子,文本框输入范围为 1 - 255 个字符,那么等价类按输入长度划分有三类 0、1 到 255、大于 255,而边界值则包括 0、1、2、254、255、256 等。

其他类似于空数组、数组的第一个和最后一个、报表的第一行和最后一行等等,也是属于边界值,需要特别关注。

其他方法

除了上面提到的几种,测试设计方法还有几种常用的:

场景法。场景法是根据模块实际使用的场景,例如 API 的实际调用方法、系统的实际需求场景和处理逻辑创建的用例。这种方法比较直观,并且用例贴近实际需求的,不可忽视。

错误推测。错误推测其实就是凭直觉,考虑最容易出错的情况来设计用例。例如,我们直到新用户、重复请求、并发、弱网、大数据量等情况都是非常容易出错的,那么可以针对性的设计用例。错误推测需要测试设计者比较熟悉业务逻辑,并且经验丰富。

其他还有因果图、正交法等方法,这里就不说了。

覆盖率

如果按照前面的用例设计方法,可能会设计出很多用例。我们不可能也没有必要把每一个用例都写成单元测试。

怎么确认用例是否足够呢?一个很重要的参考指标就是代码覆盖率。

覆盖率指标

常用的覆盖率指标有四种:

语句覆盖:每条语句至少执行一次。

分支覆盖:每个分支至少有一次为真、一次为假。

条件覆盖:每个分支的每个条件至少有一次为真、一次为假。

路径覆盖:对所有的分支、循环等可能的路径,至少都要覆盖一次。

我们以这个简单的代码为例,看看这四种覆盖率到底是什么意思。

if (a && b) {

// X

}

// Y

if (c || d) {

// X

}

语句覆盖。只需要一个测试用例,让 a && b 和 c || d 都为真,系统会依次执行 X、Y、Z 三个的代码段,就能做到语句覆盖。

分支覆盖。至少需要两个测试用例,让 a && b 和 c || d 都各为真假,例如用例1 a && b 为真和 c || d 为假,用例2 则反过来,既可让两个条件分支都各为真一次,为假一次。

条件覆盖。至少需要四个测试用例,条件 a 和 b 的四种组合都要执行一次,条件 c 和 d 的四种组合也都要执行一次。

路径覆盖。至少需要八个测试用例,条件 a、b、c 和 d 的所有组合都要执行一次。

可以看到,要做到条件覆盖甚至路径覆盖,会需要非常多的测试用例。一般情况,对于复杂的逻辑,单元测试做到分支覆盖就不错了,必要的话再做更多完全的覆盖。

Jacoco 覆盖

Jacoco 的覆盖率略有不同,这里简单说一下。

指令覆盖(Instructions),覆盖所有的 Java 代码指令。

分支覆盖(Branches),和上面的分支覆盖基本是一样的。

圈复杂度覆盖(Cyclomatic Complexity),可以认为就是路径覆盖率。

语句覆盖(Lines),和上面的语句覆盖基本是一样的。

方法覆盖(Methods),覆盖所有的方法。

类覆盖(Classes),覆盖所有的类。

怎么写有效的单元测试?

到现在,相信大家对怎么写单元测试应该有一定概念了。但是很多人也会有疑问:

单元测试耗费太多时间,会不会降低生产效率?

单元测试会不会很难维护?比如修改代码时还总是需要修改单元测试。

关于第一个问题,相信大家应该都能理解,如果我们在开发时发现 BUG,那么解决它是很容易的;但是一旦到了集成、验收甚至上线之后,那么要解决它就要花费比较大的代价了。业界很早就有共识,并且有不少数据可以证明,有效的单元测试虽然要花费更多编码时间,但是可以很大的减少项目的集成、测试和维护成本。

注意上面提到很重要一点是,单元测试必须是有效的,如果我们发现单元测试很难维护,那往往是因为我们没有写出有效的单元测试。

不是所有的代码都需要单元测试

写单元测试我们也需要考虑投入产出比,例如下面这些情况,写单元测试的投入产出比可能会较差。

短期的或者一次性的项目,例如 Demo、数据更新脚本。

业务简单的,不含太多逻辑的模块。例如获取或者查找一个数据,或者没有分支条件的业务逻辑等。

UI 层,相对而言比较难做单元测试,除非 UI 本身就有比较复杂的逻辑(其实某些 UI 框架也提供了单元测试工具)。

那么那些情况下要写单元测试呢?简单来说,就是两类。

逻辑复杂、不容易理解、容易出错的模块。例如,计算闰年的方法、订单下单等。

公共模块或者核心的业务模块。

即使对于需要写单元测试的模块,我们也应该关注最核心最重要的测试用例,而没必要单纯的追求覆盖率,或者追求条件覆盖甚至路径覆盖,一般做到分支覆盖就可以了。另外一个有效的方法是,对于出现的每一个 BUG,添加一个单元测试。

单元测试应该是稳定的

这里稳定的第一个含义是,单元测试不应该经常需要修改。如果单元测试经常因为底层实现逻辑的变动而需要修改,那一定不是好的单元测试。也就是说,被测单元的接口应该是稳定的、设计良好的、易于扩展的。

稳定的第二个含义是,单元测试的结果应该是稳定的。如果在不同的环境、不同的情况运行单元测试,会返回不同的结果,那就不是好的单元测试。如果测试需要依赖特定的数据、文件等,那需要有前置的初始化脚本确保依赖的数据、文件在所有环境都存在并且是一致的。

单元测试应该是灰盒测试

单元测试应该覆盖核心逻辑的各种分支、边界及异常,但是避免涉及易变的实现逻辑。也就是说,我们不应该把单元测试当成完全的白盒测试,但也不是黑盒测试,而应该把它当成介于白盒和黑盒之间的灰盒测试。

被测代码应该是抽象良好的

如果我们发现一段代码很难编写单元测试,常常是因为这段代码没有符合良好的抽象规范,比如没有使用 DI、不符合单一职责原则、或者依赖了全局的公共变量和方法等等。我们可以考虑优化这段代码,再来尝试单元单元测试。

谈谈到底什么是抽象,以及软件设计的抽象原则 介绍了软件抽象的原则,这里就不再重复了。

编码时就应该同时写好单元测试

这样我们才能在调试时就发挥单元测试的优势,对代码的任何修改都能得到即时反馈。如果是后面再补充单元测试,一方面对实现可能已经不太熟悉了,编写测试的代价更大了;另一方面,单元测试能发挥的作用也变小了。不过即使这样,对那些需要长远维护的项目,编写单元测试也还是很有用的。

单元测试的代码质量也很重要

单元测试也是代码,也是需要不断维护的。所以我们不应该随随便便的去写单元测试,而是要把他们也当成普通代码一样,要做到高质量、模块化、可维护。

为什么要写单元测试之终极原因

终极原因是,作为一名优秀的工程师,如果被 QA 和产品经理 Challenge 有 BUG,能忍吗?而我们工程师当然要用工程师 Style 的测试方法,那就是自动化的单元测试了,不是吗?

文章来源:网络 版权归原作者所有

上文内容不用于商业目的,如涉及知识产权问题,请权利人联系小编,我们将立即处理

相关文章:

软件测试技术之单元测试—工程师 Style 的测试方法(3)

如何设计单元测试? 单元测试设计方法 单元测试用例,和普通测试用例的设计,没有太多不同,常见的就是等价类划分、边界值分析等。而测试用例的设计其实也是开发者应该掌握的基本技能。 等价类划分 把所有输入划分为若干分类&…...

Ubuntu中安装OpenSSL

文章目录 一、前期准备1.1 压缩包下载1.2 gcc, make等的安装二、安装配置 一、前期准备 1.1 压缩包下载 在安装openssl之前,我们需要下载对应的压缩包 https://www.openssl.org/source/openssl-3.0.1.tar.gz 此压缩包可以选择win上下载后解压再复制到本地虚拟机中…...

CW4-6A-S、CW4-10A-S、CW4-20A-S、CW4-30A-S螺栓式滤波器

CW3L2-3A-S、CW3L2-6A-S、CW3L2-10A-S、CW3L2-20A-S CW3-3A-S、CW3-6A-S、CW3-10A-S、CW3-20A-S、CW3-30A-S CW4EL2-3A-S、CW4EL2-6A-S、CW4EL2-10A-SCW4EL2-20A-S、CW4EL2-30A-S CW4E-3A-S、CW4E-6A-S、CW4E-10A-S、CW4E-20A-S、CW4E-30A-S CW4E-40A-S(001)、CW4E-50A-S(0…...

课程项目设计--项目设计--宿舍管理系统--vue+springboot完成项目--项目从零开始

写在前面: 本文是从项目设计到完成开始写的,本来这个项目基础功能是做完了的,但是之前时间紧张想从头做起了。之前一周写前端后端累死了. 设计是关键,这一篇主讲设计。可能后面会有修改,本人实力有限,学习的也是别人的…...

【Linux】Linux下常用搜索命令及其常用选项小结

0x00 前言 版本信息:Ubuntu 18.04.6 LTS 最后更新日期:2023.8.18 0x01 Linux下常用搜索命令及其常用选项小结 1.find (1)find path -name filename :在指定目录path查找名为filename 文件,文件名可用*匹…...

web APIs-练习五

5秒自动关闭的广告&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"…...

MySQL——基础——外连接

一、外连接查询语法&#xff1a;(实际开发中,左外连接的使用频率要高于右外连接) 左外连接 SELECT 字段列表 FROM 表1 LEFT [OUTER] JOIN 表2 ON 条件...; 相当于查询表1(左表)的所有数据 包含 表1和表2交集部分的数据 右外连接 SELECT 字段列表 FROM 表1 RIGHT [OUTER] JOIN …...

spring boot 实现Redisson分布式锁及其读写锁

分布式锁&#xff0c;就是控制分布式系统中不同进程共同访问同一共享资源的一种锁的实现。 1、引入依赖 <dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.15.5</versio…...

java-IONIO

一、JAVA IO 1.1. 阻塞 IO 模型 最传统的一种 IO 模型&#xff0c;即在读写数据过程中会发生阻塞现象。当用户线程发出 IO 请求之后&#xff0c;内 核会去查看数据是否就绪&#xff0c;如果没有就绪就会等待数据就绪&#xff0c;而用户线程就会处于阻塞状态&#xff0c;用户线…...

Python学习笔记_基础篇(十一)_socket编程

python 线程与进程简介 进程与线程的历史 我们都知道计算机是由硬件和软件组成的。硬件中的CPU是计算机的核心&#xff0c;它承担计算机的所有任务。 操作系统是运行在硬件之上的软件&#xff0c;是计算机的管理者&#xff0c;它负责资源的管理和分配、任务的调度。 程序是运行…...

C#8.0本质论第三章--更多数据类型

C#8.0本质论第三章–更多数据类型 3.1类型的划分 一个类型要么是值类型&#xff0c;要么是引用类型。区别在于拷贝方式&#xff1a;值类型数据总是拷贝值&#xff1b;引用类型的数据总是拷贝引用。 3.1.1值类型 3.1.2引用类型 引用类型的变量存储对数据存储位置的引用。 3.…...

浅拷贝与深拷贝

作者简介&#xff1a; zoro-1&#xff0c;目前大一&#xff0c;正在学习Java&#xff0c;数据结构等 作者主页&#xff1a; zoro-1的主页 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f496; 浅拷贝与深拷贝 浅拷贝浅拷贝定义浅拷贝代码演示浅…...

背包 问题

1、背包问题 1.1、01背包 题目&#xff1a; 有n件物品和一个容量为m的背包&#xff0c;第i件物品的体积是v[ i ]&#xff0c;价值是w[ i ]&#xff0c;每件物品只有一件&#xff0c;求在不超过背包容量的前提下&#xff0c;可以放的物品的最大价值是多少 基本思路&#xff…...

蓝牙资讯|安卓将加强耳机音量监控,耳机查找功能将更加普遍

为了保护用户的听力健康&#xff0c;Android 14 将增加一项新功能&#xff0c;当用户使用耳机听音乐时&#xff0c;如果音量过高或持续时间过长&#xff0c;系统会发出警告&#xff0c;并自动降低音量。这个功能叫做“耳机音量过高警告&#xff08;headphone loud sound alert&…...

vue,element。监听快捷键粘贴图片,添加到el-upload的列表。

在①中&#xff0c;粘贴图片&#xff0c;图片能够自动添加到底下el-upload组件的文件列表②。 // 对应① <el-card><el-tooltip content"粘贴图片至此" placement"top"><input readonly class"pasteImg" paste.prevent"hand…...

时序预测 | MATLAB实现基于CNN-BiLSTM卷积双向长短期记忆神经网络的时间序列预测-递归预测未来(多指标评价)

时序预测 | MATLAB实现基于CNN-BiLSTM卷积双向长短期记忆神经网络的时间序列预测-递归预测未来(多指标评价) 目录 时序预测 | MATLAB实现基于CNN-BiLSTM卷积双向长短期记忆神经网络的时间序列预测-递归预测未来(多指标评价)预测结果基本介绍程序设计参考资料 预测结果 基本介绍…...

编织梦想:SpringBoot AOP 教程与自定义日志切面完整实战

什么是 AOP AOP 是指通过预编译方式和运行期动态代理的方式&#xff0c;在不修改源代码的情况下对程序进行功能增强的一种技术。AOP 不是面向对象编程&#xff08;OOP&#xff09;的替代品&#xff0c;而是 OOP 的补充和扩展。它是一个新的维度&#xff0c;用来表达横切问题&a…...

AssignableTypeFilter 和 AnnotationTypeFilter什么区别?

在 Spring 框架中&#xff0c;AssignableTypeFilter 和 AnnotationTypeFilter 都是用于在组件扫描过程中进行过滤的工具类&#xff0c;用于筛选出特定类型或特定注解的类。它们的主要区别在于筛选的侧重点和使用方式。 AssignableTypeFilter&#xff1a; AssignableTypeFilte…...

TCP-事件模型

#include "main.h"VOID Server_write_error() {}/*1.打开网络库 * 2.校验网络库版本 * 3.创建SOCKET * 4.绑定IP地址和端口 * 5.开始监听 * 6.创建客户端socket/接受链接 * 7.与客户端收发消息 * 8.(6.7)两步的函数accept&#xff0c;send,recv 有堵塞&#xff0c;可…...

typescript 声明文件

作用 1、为已存在js库提供类型信息&#xff0c;这样在ts项目中使用这些库时候&#xff0c;就像用ts一样&#xff0c;会有代码提示、类型保护等机制 2、项目内共享类型&#xff1a;如果多个.ts文件中都用到同一个类型&#xff0c;此时可以创建.d.ts文件提供该类型&#xff0c;…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

JavaScript基础-API 和 Web API

在学习JavaScript的过程中&#xff0c;理解API&#xff08;应用程序接口&#xff09;和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能&#xff0c;使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...