代码覆盖(英语:Code coverage)是软体测试中的一种度量,描述程式中源代码被测试的比例和程度,所得比例称为代码覆盖率。
代码覆盖是由系统化软体测试所衍生的方式。第一份出版的相关参考资料是Miller及Maloney1963年在ACM通讯上发表的论文 [1]。
代码覆盖是飞行设备进行安全认证中的考量项目之一。飞行设备相关认证的指南列在美国联邦航空管理局的文件DO-178B[2]及DO-178C[3]。 功能安全标准,如IEC 61508或ISO 26262,并未定义程式码覆盖率之需求值,只列为软体测试方法的选项,唯有航太产业标准(DO-178B)要求100%,以因应超高可靠度的产品需求,此数值对于工业或汽车产品实属理想目标。
覆盖率准则
为了量测测试套件测试软体的程度,会用一种或多种不同的覆盖率准则。
以下列出一些基本的覆盖率准则:[4]
- 函数覆盖率(Function coverage):程序中的每个函式(或副程式)都被调用了吗?
- 语句覆盖率(Statement coverage):程序中的每条语句都被执行了吗?
- 边覆盖率(Edge coverage):若用控制流图表示程式,控制流图中的每个边都被执行了吗?
- 分支覆盖率(Branch coverage):每个控制结构中(例如if和case语句)的每个分支(也称为决策到决策路径)是否均被执行?例如,给定一个if语句,其true和false分支是否均被执行?(此为边覆盖率的子集)
- 条件覆盖率(Condition coverage):也称为谓词覆盖率(predicate coverage),每一个布尔子表达式是否均被取过真值和假值?
考虑以下的C++函式:
int foo (int x, int y)
{
int z = 0;
if ((x>0) && (y>0)) {
z = x;
}
return z;
}
假设此函式是一个大型程式的一部份,且某测试用例执行到此函式:
- 函式覆盖率:只要函式foo有执行过一次,即满足函式覆盖率100%的条件。
- 语句覆盖率:若有呼叫过
foo(1,1)
,函式中每一行(包括z = x;
)都执行一次,满足语句覆盖率100%的条件。 - 分支覆盖率:若有呼叫过
foo(1,1)
及foo(0,1)
,前者会使if
的条件成立,因此z = x;
会执行,后者会使if
的逻辑运算式((x>0) && (y>0);
)不成立,因此满足分支覆盖率100%的条件。 - 条件覆盖率:若有呼叫过
foo(1,1)
、foo(1,0)
及foo(0,0)
,前二个会使(x>0)
的条件成立,而第三个会使该条件不成立,而第一个会使(y>0)
的条件成立,而后面二个会使该条件不成立,所有条件都有出现成立及不成立的情形,因此满足条件覆盖率100%的条件。
考虑以下的程式:
if a and b then
以下二个测试可以得到100%的条件覆盖率:
a=true
,b=false
a=false
,b=true
但上述的测试条件都不会使if的逻辑运算式成立,因此不符合分支覆盖的条件。
有时会需要用错误插入的方式来确保所有条件及异常处理程式都有一定的覆盖率。
在一些安全关键应用(例如飞航用的软体)中,一般会需要满足修改条件/判断覆盖(modified condition/decision coverage,简称MC/DC)的准则。此准则是条件/判断覆盖的延伸,而且每个条件都要可以独立影响判断结果的成立或不成立。例如考虑以下的程式:
if (a or b) and c then
以下的测试可满足条件/判断覆盖:
- a=true, b=true, c=true
- a=false, b=false, c=false
不过,若第一项测试中b的值改为false,不影响判断结果,第二项测试中c的值改为true,不影响判断结果,因此需要用以下的测试才能满足修改条件/判断覆盖:
- a=false, b=false, c=true
- a=true, b=false, c=true
- a=false, b=true, c=true
- a=true, b=true, c=false
其中粗体的条件表示是会影响判断结果的条件,在影响判断结果的条件中,每个变数都出现至少二次,其中至少一次其值为真,至少一次其值为假。
此覆盖率准则要求要测试逻辑运算式中的所有组合,例如上述程式的多重条件覆盖需要有以下的8个测试:
- a=false, b=false, c=false
- a=false, b=false, c=true
- a=false, b=true, c=false
- a=false, b=true, c=true
- a=true, b=false, c=false
- a=true, b=false, c=true
- a=true, b=true, c=false
- a=true, b=true, c=true
以下也是一些可能会用到的覆盖率准则:
- LCSAJ覆盖率:是否执行过每一个LCSAJ(线性代码序列和跳转)?
- JJ路径覆盖率(JJ-Path coverage):是否执行过每一个JJ路径(从跳转到跳转之间的路径,也就是JCSAJ)[5]?
- 路径覆盖率(Path coverage):是否执行过程式中所有可能的路径?
- 进入点/结束点覆盖率(Entry/exit coverage):是否执行过函式中所有可能的进入点及结束点?
- 回圈覆盖率(Loop coverage):所有回圈是否都有执行过零次、一次及一次以上的测试?
- 参数值覆盖率(Parameter Value Coverage):对于一个方法的所有参数,是否有执行过其中最常见的数值?
安全关键应用一般会要求某种特定的覆盖率要到达100%。
有些覆盖之间有相关性:例如路径覆盖就包括了判断覆盖、指令覆盖及进入点/结束点覆盖,而判断覆盖也包括了指令覆盖。
完整的路径覆盖测试多半难以实现甚至不可能实现。有个判断的程式就会有种完整路径,回圈结构可能会产生无穷种完整路径。程式中的许多路径也许是不可行的,因为也许没有受测系统的输入,使系统完整依某特定路径执行。而且已证实没有识别不可行路径的通用演算法(若有,此演算法就可以求解停机问题)[6]。实务上路径覆盖测试的软体只会试图找出随著回圈执行次数不同时,有变动的路径,设法找到“基本路径”,并要求对基本路径需达到路径覆盖的要求。
实务
目标软体是在特定环境下配合特定的选项或函式库所建立,所执行的每一个函式都会对应到原代码的机能点,代码覆盖的程序可以让程式开发者及品质保证单位可以找出程式在正常情形下不会执行或是很少执行的部份(例如异常处理程式),也帮助测试工程师确认最重要的条件(机能点)是否有测试到。测试结果可进行分析,确认哪一部份程式尚未执行到,后续再修改测试程式,对这部份进行必要的测试。上述作法的目的是为了开发一套严格且可管理的回归测试。
在配合软体开发环境进行代码覆盖率时,需考虑以下的事项:
- 软体的需要哪一种代码覆盖?其覆盖要求率为何?依严格程度来看:依序为指令覆盖、条件/判断覆盖、修改条件判断覆盖及LCSAJ覆盖,越后面的越严格。
- 会利用测试时量测到的代码覆盖率来确认受测系统达到哪些需求吗(DO-178B)?
- 目的码是否可以追溯到原代码的指令?若无法追溯到原代码,DO-178B A等级的认证会要求在组合语言层级的代码覆盖。“需在目的码层级进行额外的验证.以确认产生目的码的正确性”(DO-178B para-6.4.4.2)[2]
测试工程师可以根据代码覆盖测试的报告来调整测试用例、输入或是组态,以增加重要机能的代码覆盖率。测试工程师常常会使用到指令覆盖及判断覆盖,前者会报告在测试中会执行到多少比例的程式码,后者会报告在测试中会执行到多少比例的的判断结果,二者都会输出一个覆盖率的度量,以百分比表示。其意义则需视进行的代码覆盖种类来决定,67%的判断覆盖率会比67%的指令覆盖率会来得全面。
一般而言代码覆盖工具及函式库会影响程式性能,也会消耗记忆体或其他资源,无法在系统正常使用时测试。因此一般只在开发阶段进行,提供给客户的系统不会包括代码覆盖工具及函式库。也有一些软体无法用覆盖测试来测试,其覆盖率会用分析的方式来得到近似值,而不是用直接测试到的结果。
有一些缺陷会受到代码覆盖工具的影响,像在进行代码覆盖测试时,可以略过一些有竞争危害或是实时敏感度高的程序。相对的,当加上额外的代码覆盖测试码时,也可能比较容易找到这类的缺陷。
相关条目
参考资料
外部链接
Wikiwand in your browser!
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.