在作单元测试时,代码覆盖率经常被拿来做为衡量测试好坏的指标,甚至,用代码覆盖率来考核测试任务完成状况,好比,代码覆盖率必须达到80%或 90%。因而乎,测试人员费尽心思设计案例覆盖代码。用代码覆盖率来衡量,有利也有有弊。本文咱们就代码覆盖率展开讨论,也欢迎同窗们踊跃评论。函数
首先,让咱们先来了解一下所谓的“代码覆盖率”。我找来了所谓的定义:oop
代码覆盖率 = 代码的覆盖程度,一种度量方式。post
上面简短精悍的文字很是准确的描述了代码覆盖率的含义。而代码覆盖程度的度量方式是有不少种的,这里介绍一下最经常使用的几种:单元测试
1. 语句覆盖(StatementCoverage)
又称行覆盖(LineCoverage),段覆盖(SegmentCoverage),基本块覆盖(BasicBlockCoverage),这是最经常使用也是最多见的一种覆盖方式,就是度量被测代码中每一个可执行语句是否被执行到了。这里说的是“可执行语句”,所以就不会包括像C++的头文件声明,代码注释,空行,等等。很是好理解,只统计可以执行的代码被执行了多少行。须要注意的是,单独一行的花括号{} 也经常被统计进去。语句覆盖经常被人指责为“最弱的覆盖”,它只管覆盖代码中的执行语句,却不考虑各类分支的组合等等。假如你的上司只要求你达到语句覆盖,那么你能够省下不少功夫,可是,换来的确实测试效果的不明显,很难更多地发现代码中的问题。学习
这里举一个不能再简单的例子,咱们看下面的被测试代码:测试
{
return a / b;
}
假如咱们的测试人员编写以下测试案例:url
测试人员的测试结果会告诉你,他的代码覆盖率达到了100%,而且全部测试案例都经过了。然而遗憾的是,咱们的语句覆盖率达到了所谓的100%,可是却没有发现最简单的Bug,好比,当我让b=0时,会抛出一个除零异常。spa
正因如此,假如上面只要求测试人员语句覆盖率达到多少的话,测试人员只要钻钻空子,专门针对如何覆盖代码行编写测试案例,就很容易达到主管的要求。固然了,这同时说明了几个问题:设计
1.主管只使用语句覆盖率来考核测试人员自己就有问题。
2.测试人员的目的是为了测好代码,钻如此的空子是缺少职业道德的。
3.是否应该采用更好的考核方式来考核测试人员的工做?
为了寻求更好的考核标准,咱们必须先了解完代码覆盖率到底还有哪些,若是你的主管只知道语句覆盖,行覆盖,那么你应该主动向他介绍还有更多的覆盖方式。好比:
2. 断定覆盖(DecisionCoverage)
又称分支覆盖(BranchCoverage),全部边界覆盖(All-EdgesCoverage),基本路径覆盖(BasicPathCoverage),断定路径覆盖(Decision-Decision-Path)。它度量程序中每个断定的分支是否都被测试到了。这句话是须要进一步理解的,应该很是容易和下面说到的条件覆盖混淆。所以咱们直接介绍第三种覆盖方式,而后和断定覆盖一块儿来对比,就明白二者是怎么回事了。
3. 条件覆盖(ConditionCoverage)
它度量断定中的每一个子表达式结果true和false是否被测试到了。为了说明断定覆盖和条件覆盖的区别,咱们来举一个例子,假如咱们的被测代码以下:
{
if (a < 10 || b < 10) // 断定
{
return 0; // 分支一
}
else
{
return 1; // 分支二
}
}
设计断定覆盖案例时,咱们只须要考虑断定结果为true和false两种状况,所以,咱们设计以下的案例就能达到断定覆盖率100%:
TestCaes2: a = 15, b = 15 覆盖了分支二
设计条件覆盖案例时,咱们须要考虑断定中的每一个条件表达式结果,为了覆盖率达到100%,咱们设计了以下的案例:
TestCase4: a = 15, b = 15 false, false
经过上面的例子,咱们应该很清楚了断定覆盖和条件覆盖的区别。须要特别注意的是:条件覆盖不是将断定中的每一个条件表达式的结果进行排列组合,而是只要每一个条件表达式的结果true和false测试到了就OK了。所以,咱们能够这样推论:彻底的条件覆盖并不能保证彻底的断定覆盖。好比上面的例子,假如我设计的案例为:
TestCase1: a = 15, b = 5 false, true 分支一
咱们看到,虽然咱们完整的作到了条件覆盖,可是咱们却没有作到完整的断定覆盖,咱们只覆盖了分支一。上面的例子也能够看出,这两种覆盖方式看起来彷佛都不咋滴。咱们接下来看看第四种覆盖方式。
4. 路径覆盖(PathCoverage)
又称断言覆盖(PredicateCoverage)。它度量了是否函数的每个分支都被执行了。 这句话也很是好理解,就是全部可能的分支都执行一遍,有多个分支嵌套时,须要对多个分支进行排列组合,可想而知,测试路径随着分支的数量指数级别增长。好比下面的测试代码中有两个断定分支:
{
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. 测试人员不能盲目追求代码覆盖率,而应该想办法设计更多更好的案例,哪怕多设计出来的案例对覆盖率一点影响也没有。