代码覆盖度也是测试程序的一个重要指标,因为是对于代码内部结构的测试,所以应当是属于白盒测试的范畴,这里主要是辨析代码覆盖度的几个重要的测试标准,他们很容易混淆。
函数覆盖度:这应该是最为粗糙的测试标准了,非常宽泛,意思就是说看有百分之多少个函数被调用过,例如——
public class Demo{
public static void main(String[] args){
f1();
}
public static void f1(){
return;
}
public static void f2(){
return;
}
}
一共有三个函数,实际运行就是覆盖率main和f1,一共66.7%的覆盖度。
语句覆盖度:这个就是说,在运行一次的时候有多少条代码会被执行,比方说第一个测试用例执行了其中50%的代码,后一个测试用例只要能涵盖其余的百分之五十就可以了。这个不考虑不同的代码执行的组合会产生什么影响,它主要考虑得是每条代码是否能正常运行,或者是否实际可达。
分支覆盖度:这个其实就是分支语境下的“语句覆盖度”,他们非常类似,分支覆盖就是说if, while()这些带有条件判断的语句,布尔值的两个方向都需要被测试到,例如——
public class Demo{
public static void main(String[] args){
if(Boolean Expression1){
;//do something
}
else {
;//do something
}
if(Boolean Expression2){
;//do something
}
else{
;//do something
}
return ;
}
}
也就是说,每个Expression的真假情况都至少出现一次,他不考虑Expression1和Expression2的真假组合,所以说很类似于语句覆盖度。
条件覆盖度:还是用上面那个代码来说明,条件覆盖度就是说,设——
Expression = Cnd1 OP Cnd2 OP Cnd3..........
OP就是逻辑操作,Cnd就是各种布尔元值,条件覆盖度的意思就是每个Cnd取真取假的情况同样也要覆盖到,不考虑各个Cnd之间的组合。
重要的辨析:条件覆盖和分支覆盖有包含关系吗?
答案是否定的,且看这个例子:
if(Cnd1 || !Cnd1){
//do something
}
else{
//do something
}
这个我就可以Covers所有的条件覆盖,但是无论Cnd取哪个值,我的else都无法覆盖到,所以条件覆盖的成功无法确保分支覆盖的成功。
再来另一个方向的例子——
if(Cnd1 || (Cnd2 && !Cnd2)){
//do something
}
else{
//do something
}
这里我只需要改变Cnd1的真假就可以Covers if-else两个分支,可见分支覆盖的的成功也无法确保条件覆盖的成功。所以他们是没有包含关系的。
最后就是路径覆盖度了,这个概念是最强的,同时也是最难实现的,他强调的是每个理论上和语法上可行的代码执行路径都需要被覆盖到,这个的核心就是Covers代码执行的序列,但是这个通常是很难的,尤其是当程序中存在着循环,那完全就是一个组合指数爆炸,甚至来说计算出所有的可能性应该都是很难很复杂的,也就是最后很可能求出来了分子而不知道分母应该是个多少。
自然界的规律就大致是这样,得此失彼,不可能有什么完美的方法在各个方面都很优秀,即便存在着在性能上无懈可击的方法,看他在历史上出现的时间也可以断言有多么烧脑,编写的难度也是一个“损失”。所以我们需要作出一些妥协,在各个评判标准中作出折中。