代码覆盖率

在做单元测试时,代码覆盖率常常被拿来作为衡量测试好坏的指标,甚至,用代码覆盖率来考核测试任务完成情况,比如,代码覆盖率必须达到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. 测试人员不能盲目追求代码覆盖率,而应该想办法设计更多更好的案例,哪怕多设计出来的案例对覆盖率一点影响也没有。

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

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

相关文章

【零散技术】微信小程数组排序

序言:时间是我们最宝贵的财富,珍惜手上的每个时分 目录 一&#xff1a;业务功能 二&#xff1a;代码实现 在国内&#xff0c;微信小程序的生态已经完全渗透至各行各业&#xff0c;无一幸免。 今天分享一个常用组件的功能&#xff1a;排序 一&#xff1a;业务功能 以订单排序…

js--数组排序

微信扫码关注公众号 &#xff1a;前端前端大前端&#xff0c;追求更精致的阅读体验 &#xff0c;一起来学习啊关注后发送关键资料,免费获取一整套前端系统学习资料和老男孩python系列课程 学习资源推荐 js [1,5,3].sort(function (a,b) { return a-b; }) //[1,3,5] [1,5,3].s…

数组方法 sort() 排序错乱问题

一、问题 在JavaScript中&#xff0c;数组使用sort()后发现有排序不正确的情况&#xff0c;如下&#xff1a; let arr [1, 2, 3, 10, 20, 30] arr.sort() console.log(arr) // [1, 10, 2, 20, 3, 30] 二、原因 其实&#xff0c;sort方法会调用每个数组元素的toString方法得…

Js数组排序函数:sort()

原文链接&#xff1a;https://blog.csdn.net/qq_37936542/article/details/78979521 js原生的 sort() 排序函数使用起来很方便 语法&#xff1a;arrayObject.sort(fun) 数组对象.sort(排序函数) 如果调用该方法时没有使用参数&#xff0c;将按照字符编码的顺序进行排序。…

JS常用的6种数组排序

目录 1&#xff0c;冒泡排序2&#xff0c;快速排序3&#xff0c;插入排序4&#xff0c;选择排序5&#xff0c;希尔排序6&#xff0c;归并排序7&#xff0c;六种方法的集合 1&#xff0c;冒泡排序 冒泡排序又称为交换排序。原理是从第一个元素开始&#xff0c;比较相邻元素的大小…

JS数组排序技巧汇总(冒泡、sort、快速、希尔等排序)

本文实例总结了JS数组排序技巧。分享给大家供大家参考&#xff0c;具体如下&#xff1a; 1、冒泡排序 var temp 0; for (var i 0; i < array.length; i) { for (var j 0; j < array.length - i; j) { if (array[j] > array[j 1]) { temp array[j 1]; array[j 1…

js数组按照下标对象的属性排序

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 根据数组中某个参数的值的大小进行升序 <script type"text/javascript">function compare(val) {return function (a, b) {var value1 a[val];var value2 b[val];return value1…

JavaScript-数组乱序

前言 对数组进行排序对我们来说很容易就能够实现&#xff0c;但是你有考虑过如何对一个有序的数组实现乱序&#xff0c;即随机排序吗&#xff1f; 数组乱序在实际开发过程中是可能碰到的&#xff0c;下面我们一起看看如何实现数组乱序。 欢迎关注我的微信公众号&#xff1a;前…

微信小程序:Array数组的操作

Array 对象方法 方法描述concat()连接两个或更多的数组&#xff0c;并返回结果。copyWithin()从数组的指定位置拷贝元素到数组的另一个指定位置中。entries()返回数组的可迭代对象。every()检测数值元素的每个元素是否都符合条件。fill()使用一个固定值来填充数组。filter()检…

js 数组排序

代码改变世界 Posts - 29, Articles - 0, Comments - 62 Cnblogs Dashboard Login HOMECONTACTGALLERYRSS 那时候的我github&#xff1a;https://github.com/lwzhang js中的数组对象排序 2014-04-27 19:15 by 那时候的我, 66416 阅读, 2 评论, 收藏, 编辑 一、普通数组排序…

微信小程序——数组操作 (增加删除修改遍历)map、filter、forEach、find的用法、二维数组,排序,求和、指定长度数组赋值

一、数组的操作 Array.push() ->在数组后面继续插入内容 Array.pop() ->拿走数组最后一个内容 Array…shift()->拿走数组的第一个内容 (unshift也是拿走最后一个) Array.reverse()->对数组从大到小排列 Array.sort()->对数组从小到大排列** Array.splice(起始…

js数组排序实用方法集锦

js数组排序实用方法集锦 前言&#xff1a; 据说程序员三个月就能忘记自己写的代码&#xff0c;所以最好是在有空的时候及时做些总结&#xff0c;记录下来&#xff0c;这样后边遇到类似问题的话&#xff0c;就可以直接先查看自己的博客了。写技术博客&#xff0c;对自己是一种总…

运行 100 万个并发任务究竟需要多少内存?

Laf 公众号已接入了 AI 绘画工具 Midjourney&#xff0c;可以让你轻松画出很多“大师”级的作品。同时还接入了 AI 聊天机器人&#xff0c;支持 GPT、Claude 以及 Laf 专有模型&#xff0c;可通过指令来随意切换模型。欢迎前来调戏&#x1f447; <<< 左右滑动见更多 &…

MyBatis 环境搭建+基本使用

目录 MyBatis创建MyBatis环境搭建MyBatis模式开发MyBatis 获取动态参数&#xff08;查询操作&#xff09;${} 直接替换#{} 占位符模式替换like查询&#xff08;模糊查询&#xff09;多表查询一对一的表映射一对多的表映射 增、删、改操作改操作删除操作增加操作添加用户添加用户…

Spring事务与事务传播

文章目录 一、什么是事务?二、Spring事务实现编程式事务声明式事务 三、Transactional的使用参数作用Spring事务的隔离级别事务失效的场景Transactional工作原理 四、Spring事务传播机制Spring有哪些事务传播机制&#xff1f; 一、什么是事务? 事务&#xff1a;事务是一组操…

mybatis分页中的报错

1 Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.lang.NoSuchMethodException: com.github.pagehelper.BoundSqlInterceptor.<init>() 出错的原因就是上面的那句话 Error parsing SQL Mapper Configuration…

Mybatis分页方式及实现原理

一、mybatis的4种分页方式(物理分页、逻辑分页) 1、借助Sql语句Q进行分页(物理分页) 2、拦截器分页(物理分页)通过拦截器给sq语句末尾加Eimt语句来查询 3、借助 数组Q进行分页(逻辑分页) 4、RowBounds分页插件实现分页(逻辑分页) 二、mybatis分页的原理 mybatis分页原理是&…

Ora提示词版ChatGPT机器人

Ora可以自己创建一个ChatGPT机器人&#xff0c;可以设置自己的提示词例如我创建的AI佛祖https://ora.ai/aesthetic-red-nfa4/ai%E4%BD%9B%E7%A5%96 提示词 创建机器人的时候&#xff0c;需要设定自己的提示词&#xff0c;例如&#xff1a; 假设你是佛祖&#xff0c;名字叫做释迦…

mybatis分页查询插件

1.引入jar包 <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.3.0</version></dependency> 2.在mybatis的核心配置文件mybatis.xml中配置分页插件 3.使用pageHec publi…

Mybatis分页查询——四种传参方式

目录 相关导读 一、顺序传参 1. 持久层接口方法 2. UserMapper.xml映射文件新增标签 3. 新增测试方法 4. 运行结果 二、param传参 1. 持久层接口方法 2. UserMapper.xml映射文件新增标签 3. 新增测试方法 4. 运行结果 三、自定义POJO类传参 1. 自定义POJO类 2. 持…