代码覆盖率

2023-11-09

在做单元测试时,代码覆盖率常常被拿来作为衡量测试好坏的指标,甚至,用代码覆盖率来考核测试任务完成情况,比如,代码覆盖率必须达到80%或 90%。于是乎,测试人员费尽心思设计案例覆盖代码。用代码覆盖率来衡量,有利也有有弊。本文我们就代码覆盖率展开讨论,也欢迎同学们踊跃评论。

首先,让我们先来了解一下所谓的“代码覆盖率”。我找来了所谓的定义:

代码覆盖率 = 代码的覆盖程度,一种度量方式。

上面简短精悍的文字非常准确的描述了代码覆盖率的含义。而代码覆盖程度的度量方式是有很多种的,这里介绍一下最常用的几种:

1. 语句覆盖(StatementCoverage)

又称行覆盖(LineCoverage),段覆盖(SegmentCoverage),基本块覆盖(BasicBlockCoverage),这是最常用也是最常见的一种覆盖方式,就是度量被测代码中每个可执行语句是否被执行到了。这里说的是“可执行语句”,因此就不会包括像C++的头文件声明,代码注释,空行,等等。非常好理解,只统计能够执行的代码被执行了多少行。需要注意的是,单独一行的花括号{} 也常常被统计进去。语句覆盖常常被人指责为“最弱的覆盖”,它只管覆盖代码中的执行语句,却不考虑各种分支的组合等等。假如你的上司只要求你达到语句覆盖,那么你可以省下很多功夫,但是,换来的确实测试效果的不明显,很难更多地发现代码中的问题。

这里举一个不能再简单的例子,我们看下面的被测试代码:

int foo(int a, int b)
{
   return  a / b;
}


假如我们的测试人员编写如下测试案例:

TeseCase: a = 10, b = 5

测试人员的测试结果会告诉你,他的代码覆盖率达到了100%,并且所有测试案例都通过了。然而遗憾的是,我们的语句覆盖率达到了所谓的100%,但是却没有发现最简单的Bug,比如,当我让b=0时,会抛出一个除零异常。

正因如此,假如上面只要求测试人员语句覆盖率达到多少的话,测试人员只要钻钻空子,专门针对如何覆盖代码行编写测试案例,就很容易达到主管的要求。当然了,这同时说明了几个问题:

    1.主管只使用语句覆盖率来考核测试人员本身就有问题。

    2.测试人员的目的是为了测好代码,钻如此的空子是缺乏职业道德的。

    3.是否应该采用更好的考核方式来考核测试人员的工作?

为了寻求更好的考核标准,我们必须先了解完代码覆盖率到底还有哪些,如果你的主管只知道语句覆盖,行覆盖,那么你应该主动向他介绍还有更多的覆盖方式。比如:

2. 判定覆盖(DecisionCoverage)

又称分支覆盖(BranchCoverage),所有边界覆盖(All-EdgesCoverage),基本路径覆盖(BasicPathCoverage),判定路径覆盖(Decision-Decision-Path)。它度量程序中每一个判定的分支是否都被测试到了。这句话是需要进一步理解的,应该非常容易和下面说到的条件覆盖混淆。因此我们直接介绍第三种覆盖方式,然后和判定覆盖一起来对比,就明白两者是怎么回事了。

3. 条件覆盖(ConditionCoverage)

它度量判定中的每个子表达式结果true和false是否被测试到了。为了说明判定覆盖和条件覆盖的区别,我们来举一个例子,假如我们的被测代码如下:

int foo(int a, int b)
{
    if (a < 10 || b < 10) // 判定
    {
        return 0; // 分支一
    }
    else
    {
        return 1; // 分支二
    }
}


设计判定覆盖案例时,我们只需要考虑判定结果为true和false两种情况,因此,我们设计如下的案例就能达到判定覆盖率100%:

TestCaes1: a = 5, b = 任意数字  覆盖了分支一
TestCaes2: a = 15, b = 15          覆盖了分支二

 
设计条件覆盖案例时,我们需要考虑判定中的每个条件表达式结果,为了覆盖率达到100%,我们设计了如下的案例:

TestCase1: a = 5, b = 5       true,  true
TestCase4: a = 15, b = 15   false, false


通过上面的例子,我们应该很清楚了判定覆盖和条件覆盖的区别。需要特别注意的是:条件覆盖不是将判定中的每个条件表达式的结果进行排列组合,而是只要每个条件表达式的结果true和false测试到了就OK了。因此,我们可以这样推论:完全的条件覆盖并不能保证完全的判定覆盖。比如上面的例子,假如我设计的案例为:

TestCase1: a = 5, b = 15  true,  false   分支一
TestCase1: a = 15, b = 5  false, true    分支一

 
我们看到,虽然我们完整的做到了条件覆盖,但是我们却没有做到完整的判定覆盖,我们只覆盖了分支一。上面的例子也可以看出,这两种覆盖方式看起来似乎都不咋滴。我们接下来看看第四种覆盖方式。

4. 路径覆盖(PathCoverage)

又称断言覆盖(PredicateCoverage)。它度量了是否函数的每一个分支都被执行了。 这句话也非常好理解,就是所有可能的分支都执行一遍,有多个分支嵌套时,需要对多个分支进行排列组合,可想而知,测试路径随着分支的数量指数级别增加。比如下面的测试代码中有两个判定分支:

int foo(int a, int b)
{
    int nReturn = 0;
    if (a < 10)
    {// 分支一
        nReturn += 1;
    }
    if (b < 10)
    {// 分支二
        nReturn += 10;
    }
    return nReturn;
}


对上面的代码,我们分别针对我们前三种覆盖方式来设计测试案例:

a. 语句覆盖

TestCase a = 5, b = 5   nReturn = 11

 语句覆盖率100%

b. 判定覆盖

TestCase1 a = 5,   b = 5     nReturn = 11

TestCase2 a = 15, b = 15   nReturn = 0

判定覆盖率100%

c. 条件覆盖

TestCase1 a = 5,   b = 15   nReturn = 1

TestCase2 a = 15, b = 5     nReturn = 10

条件覆盖率100%

 

我们看到,上面三种覆盖率结果看起来都很酷!都达到了100%!主管可能会非常的开心,但是,让我们再去仔细的看看,上面被测代码中,nReturn的结果一共有四种可能的返回值:0,1,10,11,而我们上面的针对每种覆盖率设计的测试案例只覆盖了部分返回值,因此,可以说使用上面任一覆盖方式,虽然覆盖率达到了100%,但是并没有测试完全。接下来我们来看看针对路径覆盖设计出来的测试案例:

TestCase1 a = 5,    b = 5     nReturn = 0

TestCase2 a = 15,  b = 5     nReturn = 1

TestCase3 a = 5,    b = 15   nReturn = 10

TestCase4 a = 15,  b = 15   nReturn = 11

路径覆盖率100%


太棒了!路径覆盖将所有可能的返回值都测试到了。这也正是它被很多人认为是“最强的覆盖”的原因了。

还有一些其他的覆盖方式,如:循环覆盖(LoopCoverage),它度量是否对循环体执行了零次,一次和多余一次循环。剩下一些其他覆盖方式就不介绍了。

总结

通过上面的学习,我们再回头想想,覆盖率数据到底有多大意义。我总结了如下几个观点,欢迎大家讨论:

a. 覆盖率数据只能代表你测试过哪些代码,不能代表你是否测试好这些代码。(比如上面第一个除零Bug)

b. 不要过于相信覆盖率数据。

c. 不要只拿语句覆盖率(行覆盖率)来考核你的测试人员。

d. 路径覆盖率 > 判定覆盖 > 语句覆盖

e. 测试人员不能盲目追求代码覆盖率,而应该想办法设计更多更好的案例,哪怕多设计出来的案例对覆盖率一点影响也没有。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

代码覆盖率 的相关文章

  • SpringBoot框架

    目录 1 1 简介 1 2 特性 1 3 四大核心 2 springboot入门案例 2 1 SpringBoot 项目开发步骤 2 2 创建一个 Spring MVC 的 Spring BootController 2 3 分析 2 4
  • 如何从0到1做一次完整的安全测试

    大家好 我是馨馨 一个混过大厂 待过创业公司 有着6年工作经验的软件测试妹纸一枚 近期针对公司项目做了一次完整的安全测试 扫描出来了不少漏洞 价值还挺大的 回顾整个流程 并没有特别复杂的点 小林星球这里程序员还挺多 想着分享下我的实战感悟
  • python -图例设置

    1 使用方法 方法1 ax legend line1 line2 line3 label1 label2 label3 方法2 line1 ax plot 1 2 3 label label1 line2 ax plot 1 2 3 lab
  • swift4.0 项目中的随机校验码(验证码)

    在开发中注册的时候移动端要求我们获取随机校码 完整代码 class CodeView UIView var changeString String 验证码的字符串 let kLineCount 6 let kLineWidth CGFloa
  • Anaconda入门基本操作

    文章目录 1 配置环境变量 2 基础操作 2 1 activate 2 2 创建自己的虚拟环境 2 3 切换环境 3 安装 卸载第三方包 4 查看环境包信息 5 导入导出环境 6 删除环境 慎用 7 总结当前命令 1 配置环境变量 如果是w
  • 数据仓库实施

    文章目录 一 数据分析挖掘过程 1 数据获取 2 数据处理 3 数据建模 4 模型评价 5 数据可视化 二 ETL过程 1 数据特征 2 过程描述 3 ETL 1 数据抽取 2 数据清洗 3 数据转换 4 数据加载和索引 4 kettle
  • git项目管理,这些就够了

    环境 ubuntu16 04 参考 https baijiahao baidu com s id 1621620608602705821 wfr spider for pc https www cnblogs com Sharley p 6

随机推荐

  • 关于前端在一个选择框中选择多个内容的操作

    直接上效果图 以上效果为 可选择多个内容放在框里 再作相应操作将内容传给后端 一 在html的body的form表单下码一个div
  • qt usb热插拔,windows版本,环境qt5.12.9和win10

    写文章的目的 自己要做windows上的usb通信 但是找遍全网都没找到完整的 虽然有热插拔相关的 但是热插拔的监测不够全面 基本都是只能监测部分的插拔 并且大家都推荐使用libusb库 但是使用了一下该库 哎哟我去 暂时还不支持windo
  • linux中shell的小括号、大括号的用法区别

    Linux中小括号 和大括号 都是对其中的一串命令进行执行 但有一定的区别 其区别如下 1 小括号 命令组 括号中的命令新开一个子shell程序 括号中的变量为本地变量 不能够在脚本其他部分使用 括号中多个命令之间用分号隔开 备注 在括号中
  • ffmpeg推流命令

    ffmpeg re i xxx mp4 vcodec copy acodec copy f flv y rtmpurl re 按照帧率发送 如果不加 手册里说的是发送as fast as possible i 输入 vcodec copy
  • 100天精通Python(进阶篇)——第42天:pdfplumber读取pdf(基础+代码实战写入Excel)

    文章目录 一 Python操作PDF 13大库对比 二 pdfplumber模块 1 安装 2 加载PDF 3 pdfplumber PDF类 4 pdfplumber Page类 三 实战操作 1 提取单个PDF全部页数 2 批量提取多个
  • LeetCode:217(Python)—— 存在重复元素(简单)

    存在重复元素 概述 给你一个整数数组 nums 如果任一值在数组中出现 至少两次 返回 true 如果数组中每个元素互不相同 返回 false 输入 nums 1 2 3 1 输出 true 输入 nums 1 2 3 4 输出 false
  • JS面试题

    1 es6新特性 let和const关键字 箭头函数 多行字符串 解构赋值 Promises let声明变量和const声明常量 变量不能重复声明 都是块级作用域 不存在变量提升 箭头函数 es6提供了简洁的箭头函数语法 可以更简单的定义函
  • python与爬虫的关系_python为什么叫爬虫?有什么关系?

    今天 小编听到有人问 Python为什么叫爬虫 我想很多人对于这个问题都很好奇 甚至对于Python和爬虫的概念模糊 今天小编通过这篇文章为大家详细解答一下 Python与爬虫有什么关系 爬虫一般是指网络资源的抓取 因为Python的脚本特
  • 误删li64.so.6,如何恢复

    遇到下面这种情况 误删li64 so 6还原方案 第一步 先关掉虚拟机 右键找到电源后选择打开电源时进入固件 进入Boot界面 第二步 使用shift 将光盘启动优先 然后切至exit保存退出 第三步 来到重启界面后 选择Troublesh
  • 微信小程序的五种传值方式

    1 使用全局变量传递数据 利用app js 中的 globalData 将数据存储为全局变量 在需要使用的页面通过getApp globalData获取数据 app js App globalData data name demo 使用组件
  • < Linux >:Linux 进程概念 (4)

    目录 五 孤儿进程 六 进程优先级 6 1 基本概念 6 2 查看时实系统进程 6 3 PRI and NI 七 其他概念 四 X 状态 死亡状态 对应于各个操作系统下的进程状态中的进程终止 退出 态 所谓进程处于 X 状态 死亡状态 代表
  • 【HTML】讲讲HTML5视频播放的方式

    HTML 讲讲HTML5视频播放的方式 引言 github HTML 讲讲HTML5视频播放的方式 内容速递 看了本文您能了解到的知识 想要网页播放视频再也不需要使用插件了 HTML本身就支持 而且更加稳定 上节讲了HTML5的音频播放 和
  • 7.14资产收集-灯塔

    sql注入 xss 源码泄露 redis未授权访问 逻辑漏洞 支付漏洞 GitHub TophantTechnology ARL ARL Asset Reconnaissance Lighthouse 资产侦察灯塔系统旨在快速侦察与目标关联
  • 在matlab中如何使用SVM工具箱

    一 SVM下载 http see xidian edu cn faculty chzheng bishe indexfiles indexl htm 二 在MATLAB中添加svm工具箱 参考 http jingyan baidu com
  • 银行业法律法规与综合能力 第五章 银行监管与自律 10%

    第五章 银行监管与自律 考点1 银行监管体制 考点2 银行自律与市场约束 考点3 职业操守与行为准则 考点4 清廉金融 考点5 银行业消费者杈益保护 考点1 银行监管体制 一 银行监管起源与演变 1 英国颁布的 反泡沫公司法 标志着世界金融
  • 小程序---云函数npm install报错

    问题 云函数右键菜单 在外部终端窗口中打开 npm install报错 解决方法 使用cnpm install 安装依赖
  • error:03000086:digital envelope routines::initialization error

    项目背景 前端vue项目启动突然报错error 03000086 digital envelope routines initialization error 我用的开发工具是vscode node版本是v18 17 0 前端项目版本如下
  • redis安装、配置、启动

    一 redis默认安装位置 usr local bin redis benchmark 性能测试工具 可以在自己本子运行 看看自己本子性能如何 服务启动起来后执行redis check aof 修复有问题的AOF文件redis check
  • mac安装VMware Fusion及虚拟Windows注意事项

    mac安装VMware Fusion及虚拟Windows注意事项 文章目录 mac安装VMware Fusion及虚拟Windows注意事项 1 前言及注意事项 2 VMWare Fusion下载及安装 3 安装虚拟Windows 4 Vi
  • 代码覆盖率

    在做单元测试时 代码覆盖率常常被拿来作为衡量测试好坏的指标 甚至 用代码覆盖率来考核测试任务完成情况 比如 代码覆盖率必须达到80 或 90 于是乎 测试人员费尽心思设计案例覆盖代码 用代码覆盖率来衡量 有利也有有弊 本文我们就代码覆盖率展