在学习极客时间的测试52讲中,了解到其实代码覆盖率的具体含义如下:
简单来说,代码覆盖率是指,至少被执行了一次的条目数占整个条目数的百分比。
如果“条目数”是语句,对应的就是代码行覆盖率;如果“条目数”是函数,对应的就是函
数覆盖率;如果“条目数”是路径,那么对应的就是路径覆盖率。依此类推,你就可以得到
绝大多数常见的代码覆盖率类型的定义。
这里我给你简单介绍一下最常用的三种代码覆盖率指标。
行覆盖率又称为语句覆盖率,指已经被执行到的语句占总可执行语句(不包含类似 C++
的头文件声明、代码注释、空行等等)的百分比。这是最常用也是要求最低的覆盖率指
标。实际项目中通常会结合判定覆盖率或者条件覆盖率一起使用。
判定覆盖又称分支覆盖,用以度量程序中每一个判定的分支是否都被测试到了,即代码中
每个判断的取真分支和取假分支是否各被覆盖至少各一次。比如,对于 if(a>0 &&
b>0),就要求覆盖“a>0 && b>0”为 TURE 和 FALSE 各一次。
条件覆盖是指,判定中的每个条件的可能取值至少满足一次,度量判定中的每个条件的结
果 TRUE 和 FALSE 是否都被测试到了。比如,对于 if(a>0 && b>0),就要
求“a>0”取 TRUE 和 FALSE 各一次,同时要求“b>0”取 TRUE 和 FALSE 各一次。
代码覆盖率的价值
现在很多项目都在单元测试以及集成测试阶段统计代码覆盖率,但是我想说的是,统计代码
覆盖率仅仅是手段,你必须透过现象看到事物的本质,才能从根本上保证软件整体的质量。
统计代码覆盖率的根本目的是找出潜在的遗漏测试用例,并有针对性的进行补充,同时还可
以识别出代码中那些由于需求变更等原因造成的不可达的废弃代码。
通常我们希望代码覆盖率越高越好,代码覆盖率越高越能说明你的测试用例设计是充分且完
备的,但你也会发现测试的成本会随着代码覆盖率的提高以类似指数级的方式迅速增加。
如果想达到 70% 的代码覆盖率,你可能只需要 30 分钟的时间成本。但如果你想把代码覆
盖率提高到 90%,那么为了这额外的 20%,你可能花的时间就远不止 30 分钟了。更进一
步,你如果想达到 100% 的代码覆盖率,可想而知你花费的代价就会更大了。
那么,为什么代码覆盖率的提高,需要付出越来越大的代价呢?因为在后期,你需要大量的
桩代码、Mock 代码和全局变量的配合来控制执行路径。
所以,在软件企业中,只有单元测试阶段对代码覆盖率有较高的要求。因为从技术实现上
讲,单元测试可以最大化地利用打桩技术来提高覆盖率。而你如果想在集成测试或者是
GUI 测试阶段将代码覆盖率提高到一定量级,那你所要付出的代价是巨大的,而且在很多
情况下根本就实现不了。
代码覆盖率的局限性
我先来问你一个问题,如果你通过努力,已经把某个函数的 MC/DC 代码覆盖率(MC/DC
覆盖率是最高标准的代码覆盖率指标,除了直接关系人生命安全的软件以外,很少会有项目
会有严格的 MC/DC 覆盖率要求)做到了 100%,软件质量是否就真的高枕无忧、万无一
失了呢?
很不幸,即使你所设计的测试用例已经达到 100% 的代码覆盖率,软件产品的质量也做不
到万无一失。其根本原因在于代码覆盖率的计算是基于现有代码的,并不能发现那些“未考
虑某些输入”以及“未处理某些情况”形成的缺陷。
我给你举个极端的例子,如果一个被测函数里面只有一行代码,只要这个函数被调用过了,
那么衡量这一行代码质量的所有覆盖率指标都会是 100%,但是这个函数是否真正实现了应
该需要实现的功能呢?
显然,代码覆盖率反映的仅仅是已有代码的哪些逻辑被执行过了,哪些逻辑还没有被执行
过。以此为依据,你可以补充测试用例,可以去测试那些还没有覆盖到的执行路径。但也是
仅此而已,对于那些压根还没有代码实现的部分,基于代码覆盖率的统计指标就无能为力
了。
总结来讲,高的代码覆盖率不一定能保证软件的质量,但是低的代码覆盖率一定不能能保证
软件的质量。
好了,现在你已经了解了代码覆盖率的概念、价值和局限性,那么接下来,我就以 Java 代
码覆盖率工具为例,给你解释一下代码覆盖率工具的内部实现原理以及一些关键技术。
当你理解了这部分内容,以后再面对各个不同开发语言的不同代码覆盖率工具时,就可以做
到胸有成竹地根据具体的项目性质,选择最合适的代码覆盖率工具了。
代码覆盖率工具
JaCoCo 是一款 Java 代码的主流开源覆盖率工具,可以很方便地嵌入到 Ant、Maven 中,
并且和很多主流的持续集成工具以及代码静态检查工具,比如 Jekins 和 Sonar 等,都有很
好的集成。
首先,我先带你看看 JaCoCo 的代码覆盖率报告长什么样子。
如图 1 所示为 JaCoCo 的整体代码覆盖率统计报告,包括了每个 Java 代码文件的行覆盖率
如图 2 所示为每个 Java 文件内部详细的代码覆盖率情况,图中绿色的行表示已经被覆盖,
红色的行表示尚未被覆盖,黄色的行表示部分覆盖;左侧绿色菱形块表示该分支已经被完全
覆盖、黄色菱形块表示该分支仅被部分覆盖。