如何提高代码的稳定性与可靠性------代码覆盖率浅谈

标签:
覆盖率测试人员代码判定分支杂谈 |
分类: 稳定性与可靠性 |
代码覆盖率浅谈
首先,让我们先来了解一下所谓的“代码覆盖率”。我找来了所谓的定义:
代码覆盖率 = 代码的覆盖程度,一种度量方式。
上面简短精悍的文字非常准确的描述了代码覆盖率的含义。而代码覆盖程度的度量方式是有很多种的,这里介绍一下最常用的几种:
1. 语句覆盖(StatementCoverage)
又称行覆盖(LineCoverage),段覆盖(SegmentCoverage),基本块覆盖(BasicBlockCoverage),这是最常用也是最常见的一种覆盖方式,就是度量被测代码中每个可执行语句是否被执行到了。这里说的是“可执行语句”,因此就不会包括像C++的头文件声明,代码注释,空行,等等。非常好理解,只统计能够执行的代码被执行了多少行。需要注意的是,单独一行的花括号{}
也常常被统计进去。语句覆盖常常被人指责为“最弱的覆盖”,它只管覆盖代码中的执行语句,却不考虑各种分支的组合等等。假如你的上司只要求你达到语句覆盖,那么你可以省下很多功夫,但是,换来的确实测试效果的不明显,很难更多地发现代码中的问题。
这里举一个不能再简单的例子,我们看下面的被测试代码:
假如我们的测试人员编写如下测试案例:
测试人员的测试结果会告诉你,他的代码覆盖率达到了100%,并且所有测试案例都通过了。然而遗憾的是,我们的语句覆盖率达到了所谓的100%,但是却没有发现最简单的Bug,比如,当我让b=0时,会抛出一个除零异常。
正因如此,假如上面只要求测试人员语句覆盖率达到多少的话,测试人员只要钻钻空子,专门针对如何覆盖代码行编写测试案例,就很容易达到主管的要求。当然了,这同时说明了几个问题:
为了寻求更好的考核标准,我们必须先了解完代码覆盖率到底还有哪些,如果你的主管只知道语句覆盖,行覆盖,那么你应该主动向他介绍还有更多的覆盖方式。比如:
2. 判定覆盖(DecisionCoverage)
又称分支覆盖(BranchCoverage),所有边界覆盖(All-EdgesCoverage),基本路径覆盖(BasicPathCoverage),判定路径覆盖(Decision-Decision-Path)。它度量程序中每一个判定的分支是否都被测试到了。这句话是需要进一步理解的,应该非常容易和下面说到的条件覆盖混淆。因此我们直接介绍第三种覆盖方式,然后和判定覆盖一起来对比,就明白两者是怎么回事了。
3. 条件覆盖(ConditionCoverage)
它度量判定中的每个子表达式结果true和false是否被测试到了。为了说明判定覆盖和条件覆盖的区别,我们来举一个例子,假如我们的被测代码如下:
{
}
设计判定覆盖案例时,我们只需要考虑判定结果为true和false两种情况,因此,我们设计如下的案例就能达到判定覆盖率100%:
设计条件覆盖案例时,我们需要考虑判定中的每个条件表达式结果,为了覆盖率达到100%,我们设计了如下的案例:
通过上面的例子,我们应该很清楚了判定覆盖和条件覆盖的区别。需要特别注意的是:条件覆盖不是将判定中的每个条件表达式的结果进行排列组合,而是只要每个条件表达式的结果true和false测试到了就OK了。因此,我们可以这样推论:完全的条件覆盖并不能保证完全的判定覆盖。比如上面的例子,假如我设计的案例为:
我们看到,虽然我们完整的做到了条件覆盖,但是我们却没有做到完整的判定覆盖,我们只覆盖了分支一。上面的例子也可以看出,这两种覆盖方式看起来似乎都不咋滴。我们接下来看看第四种覆盖方式。
4. 路径覆盖(PathCoverage)
又称断言覆盖(PredicateCoverage)。它度量了是否函数的每一个分支都被执行了。
这句话也非常好理解,就是所有可能的分支都执行一遍,有多个分支嵌套时,需要对多个分支进行排列组合,可想而知,测试路径随着分支的数量指数级别增加。比如下面的测试代码中有两个判定分支:
{
}
对上面的代码,我们分别针对我们前三种覆盖方式来设计测试案例:
a. 语句覆盖
b. 判定覆盖
c. 条件覆盖
我们看到,上面三种覆盖率结果看起来都很酷!都达到了100%!主管可能会非常的开心,但是,让我们再去仔细的看看,上面被测代码中,nReturn的结果一共有四种可能的返回值:0,1,10,11,而我们上面的针对每种覆盖率设计的测试案例只覆盖了部分返回值,因此,可以说使用上面任一覆盖方式,虽然覆盖率达到了100%,但是并没有测试完全。接下来我们来看看针对路径覆盖设计出来的测试案例:
TestCase1 a
TestCase2 a
TestCase3 a
TestCase4 a
路径覆盖率100%
太棒了!路径覆盖将所有可能的返回值都测试到了。这也正是它被很多人认为是“最强的覆盖”的原因了。
还有一些其他的覆盖方式,如:循环覆盖(LoopCoverage),它度量是否对循环体执行了零次,一次和多余一次循环。剩下一些其他覆盖方式就不介绍了。
总结
通过上面的学习,我们再回头想想,覆盖率数据到底有多大意义。我总结了如下几个观点,欢迎大家讨论:
a. 覆盖率数据只能代表你测试过哪些代码,不能代表你是否测试好这些代码。(比如上面第一个除零Bug)
b. 不要过于相信覆盖率数据。
c. 不要只拿语句覆盖率(行覆盖率)来考核你的测试人员。
d. 路径覆盖率 > 判定覆盖 > 语句覆盖
e. 测试人员不能盲目追求代码覆盖率,而应该想办法设计更多更好的案例,哪怕多设计出来的案例对覆盖率一点影响也没有。
作者:CoderZh(CoderZh的技术博客 - 博客园)
微博:http://t.sina.com.cn/coderzh
出处:http://coderzh.cnblogs.com
文章版权归本人所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
它说是多少就多少。
在我们小组的测试中,将测试数据的设计放在测试的第一位,今天看见你的文章,使我产生了共鸣的感觉。
不知道你能否在测试数据的构造上分享一下你的经验。目前,我们小组拟设计一个框架专门用于各种测试数据的维护,不知道你对此有何看法。
希望能够得到你的回复,谢谢。
但是它能反应你在测试上花费的注意力和努力,它能反应那些代码缺少测试。测试覆盖率高是好事,覆盖率低就要问一问自己为什么了。虽然不能直接表现质量,但是可以间接作为质量的参考,或者是警报。毕竟衡量质量的方法不止一种,不能只依赖其中的某一种,而是要相互结合
我以前做过这类测试项目,60个人干了一个月~不仅要编写测试代码测试覆盖率,如果发现错误还得修正错误~要不是客户要求要这个东西,一般都不去搞的~~累死人~
我也以前也做了一下代码覆盖率,不过没总结,惭愧一下,我的感觉是代码覆盖率只是一种辅助的手段,它的作用是告诉测试人员还有哪些代码你没有考虑到,最重要还是你对测试案例的设计。
我做模块接口的代码覆盖率时,开始的时候并没有直接看代码,而是根据几口的功能和参数设计测试案例,然后再做覆盖率,看有什么地方没有测到,考虑是否要追加测试案例。
最后,我的想法是代码覆盖率还是很重要的,我这样说是因为有两点:1.程序的风险很可能就存在于代码覆盖率没有涉及的地方。2.汇报工作时,我们需要以量化的方式给别人看,现在的老大们整天以代码覆盖率去考核测试人员,就是因为以上原因,但注意,他这样做是建立在对你信任的基础上的,就是你说的职业道德,对于想蒙混过关的人,我只能说一句:出来混,迟早要还~~~~
路径覆盖100%只是理论上存在 带循环和多线程的实际环境中 只能覆盖极小部分路径
谢谢你的回复
可以看出,你们小组做的非常不错!说实话,我在“测试数据的构造上”经验不多,你能否介绍一下你们专门用于各种测试数据的维护的框架呢?
正如其他人回复所说的,代码覆盖率也不是一无是处。
从你的经历中可以看出,当代码覆盖率被当做考核的重要标准时,它的意义也丧失了许多。
关键有些东西不好量化~~~代码覆盖率应该建立在良好的案例s的基础上,否则又回到的了一个关于测试人员水平的基础问题:测试案例s设计是否良好~~~~你是不是有什么想法啊?
正如“我开始做减法"所说,他们将”测试数据的设计放在测试的第一位“是很有道理的。我觉得对案例设计的质量,能否发现高质量的BUG方面进行考核效果会更好。因为可能存在这样的极端:
1.代码覆盖率100%,却没有发现一个有价值的BUG
2.代码覆盖率不高,但通过很好的案例设计,发现了很有价值的BUG。
当然,我也没有完全反对使用”代码覆盖率“作为考核标准。
飞林沙: 在国内,又有多少公司有精力去做这样全面的白盒测试呢?
--------------------------------------------------------
在我看来,现在很多测试的文章都只是空中楼阁,想法很好,实践起来就知道太难了,一个小项目要搞成高单元测试覆盖率,那是相当相当困难且费时间。
施杨: 测试很重要,不过俺确很少测,这就是专业与业余的区别吧。
--------------------------------------------------------
这也不能就说你不专业了,只是不同的项目对于质量的要求不一样而已。
如果是做外包项目,除非客户要求很高或者客户懂这些,质量要求自然不高,尽快交货就是了,所以测试搞得很少或者干脆搞个手工测试人员点点,写写文档就罢了。
如果是做自己的产品,那时候质量的问题就不得不重视了。