现在的位置: 首页 > 编程语言 > 正文

【软件测试】代码覆盖率分析

2019年03月22日 编程语言 ⁄ 共 2861字 ⁄ 字号 评论关闭

无论是单元测试、API测试还是功能性测试,最终都是调用了产品的代码;如何评价这些测试的效率,是否真正全部或者大部分覆盖了产品的代码,这个时候,代码覆盖率(code coverage)就是一个比较有价值的参考指标了。

通常,代码覆盖率用在如下几个方面
  • 找出程序中没有被测试代码执行到的地方;
  • 增加新的测试代码,以提高代码覆盖率;
  • 分析测试代码的效率,以便设计出更有效的测试代码或测试用例
代码覆盖率常用的指标

语句覆盖(statement coverage):语句覆盖是指程序的每一行代码是否都被覆盖到;语句覆盖是最常用的一种代码覆盖率指标,也非常简单。但是对一些控制结构的代码而言,它不能真正表示是否完全覆盖到,如下代码:

int* p=NULL;
if(condition)
    p=&variable;
*p=123;

在以上代码中,如果condition为true,这时,语句覆盖率就为100%。而其实当condtion为false,会导致空指针被赋值的运行时错误。

 

决策覆盖(decision coverage):决策覆盖是指在控制结构的代码块,如if, while中的整个bool表达式是否在false和true条件下,各被执行一次。决策覆盖不考虑组成该条件表达式的各个子条件是否被完全覆盖到。于是,通常对于c/c++/java语言,由于short circuit operators(即短路运算符。一般有:&&、||,其原理是:当有多个表达式时,左边的表达式值可以确定结果时,就不再继续运算右边的表达式的值),对于一个由多个条件以与关系组成的一个条件表达式,当出现第一个条件被检测为false时,其后所有的条件都不会被执行了。考虑一下代码:

if(condition1 && (condition2 || function1()))
    statement1;
else
    statement2;

在condtion1为false和conditon1为true,condition2为true,其决策覆盖率为100%,而function1中的代码从未被执行。决策覆盖也叫分支覆盖(branch coverage)。

条件覆盖(condition coverage):条件覆盖是指每个条件都必须有true和false的情况,这里一个条件是一个逻辑操作符的操作数,但是不含该逻辑操作符。注意和决策覆盖的区别,如

bool f(bool e){    return false;    }
bool a[2]={false, false};
if(f(a&&b)) ...
if(a[int(a&&b)]) ...
if((a&&b)?false:false) ...

该代码块的3个if条件中,一共包含2个条件a,b;共四种情况a=true, a=false, b=true, b=false;我们只需取a,b分别为true和false的组合(a=true, b=false和a=false, b=true)即可使条件覆盖为100%;而实际上,无论a,b取什么值,3个if语句的分支始终都返回false。

多重条件覆盖(multiple condition coverage):多重条件覆盖是指各个条件的每个可能的组合是否被检查到。多重条件覆盖中的各个条件组合是一种排列关系,而不是组合。通常多重条件覆盖的测试案例设计都非常复杂,因为在考虑到各个条件之间的排列关系时,其数量本身就多,而且还要考虑关系之间的相互影响,剔除无效的排列。

 

条件/决策覆盖(condition/decision coverage):条件决策覆盖是条件覆盖和决策覆盖的联合运算,它具有比多重条件覆盖简单的优势,同时又没有条件覆盖和决策覆盖的缺点。

 

改进的条件/决策覆盖(modified condition/decision coverage):其定义是程序中的每个入口和出口都至少被调用一次,一个决策的每个条件都至少有一次所有可能的输出,程序中的每个决策都至少有一次所有可能的输出,一个决策中的每个条件都能独立的影响该决策的输出(该条件改变,而其他条件保持不变)。

 

路径覆盖(path coverage):路径决策指示函数中的每个可能的路径是否被执行到。一条路径是一个唯一的从函数入口到出口的分支序列。路径覆盖通常能比较彻底的进行测试,但是它也有两个非常严重的缺陷:其一,路径的数量是分支数量的几何级数。例如,一个具有10个if语句的函数,需要1024个路径测试;而我们再加一条if语句,则有2048个路径需要测试。其次,由于数据相互关系,有些路径是不可能被测试到的,如:

if(success)
    statement1;
statement2;
if(success)
    statement3;

这段代码包含4条路径。而实际上,只有2条是可行的,success=false和success=true。

代码覆盖率的其他指标

其他代码覆盖率的指标还有函数覆盖率(function coverage);调用覆盖率(call coverage);循环覆盖率(loop coverage)等等。

代码覆盖率之间的关系
  • 决策覆盖通常是包含语句覆盖的,因为每个分支的执行必然需要每个语句的执行。但是,如果函数的控制流被throw,abort等语句打断,那么该规则就不适用了。
  • 条件/决策覆盖包含条件覆盖和决策覆盖。
  • 路径覆盖包含决策覆盖。
该如何利用代码覆盖率的数据

通常,我们都希望用较少的时间设计出高效的测试案例。简单的来说,高效的测试案例就是能尽可能多的发现程序中的问题。测试所花的时间、覆盖率和检测出的时间可参考下面的图表:

即便代码覆盖率为100%,我们只能说程序中的每行代码都被执行到了。但是如果我们一定要有一个定量的指标,可参考下表:

Coverage type without dependency injection with dependency injection
Function 90% 99%
Line 75% 95%

ref: http://blogs.msdn.com/b/cellfish/archive/2008/06/16/code-coverage.aspx

Dependency Injection(DI)是一种设计模式,即把一个对象引入到一个类中,而不是依靠类来创建对象本身。具体可参考:http://msdn.microsoft.com/en-us/magazine/cc163739.aspx

值得注意的是:

  • 不要一味的追求数据,为了提高代码测试覆盖率而去设计测试案例;
  • 而要以代码测试覆盖率为参考,让它来帮助我们设计更加有意义,高效的测试用例;

如果你或者你的老板一定要一个非常漂亮的数字,或者你用它来考量测试人员的绩效,那么对于.net平台上的程序,微软有一个叫Pex的工具,可以帮助你生成高代码覆盖率的测试案例。具体可参考:

http://research.microsoft.com/en-us/projects/pex/

 

注:本文大部分参考Code Coverage Analysis,欢迎转载,但请注明出处。谢谢。

抱歉!评论已关闭.