网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
了解详情》docs.qq.com/doc/DSlVlZExWQ0FRSE9H
代码覆盖率的价值
统计代码覆盖率的核心目的在于找到存在未被测试到的代码段,并对其进行有针对性的补充相关测试用例。同时,它也能有效地识别出那些因需求变更等情况而导致无法访问的废弃代码。
在当前的许多项目中,单元测试和集成测试阶段都会进行代码覆盖率的统计。然而,我想要强调的是,这只是一种方法,我们需要透过表面现象,理解其深层含义,从而能够从根本上保障软件的全面质量。
我们一般倾向于追求尽可能高的代码覆盖率,因为这代表着我们的测试用例设计得更为全面和完整。但同时,我们也会注意到,随着代码覆盖率的提高,测试的成本也会以类似于指数级别的方式迅速增加。
如果公司要对覆盖率有要求,一般公司都是有一个大家沟通好的阈值,例如对于开发来说,单元测试的覆盖率为85%,只要达到这个值就可以了,而不是去追求 100% 的覆盖率,作为这个阈值的制定者需要考虑成本、收益。对于达到85%的代码覆盖率可能只需要你投入1 个小时的时间,开发就能完成了。然而,如果你希望将代码覆盖率提升至95%,为了这额外的10%覆盖率,你可能需要投入的时间远超过一个小时。更进一步,如果你的目标是实现100%的代码覆盖率,无疑,你需要付出的努力和时间将会更多。
那么,为何提升代码覆盖率的过程中,我们需要投入越来越多的资源呢?原因在于在后期,我们需要利用大量的桩代码、Mock代码以及特殊数据、逻辑来进行指定的逻辑运行。换句话说,我们需要付出更多的努力来保持代码覆盖率的提升。
根据分层测试里面的理论,我们都知道在单元测试阶段,ROI 是最高的。详细的可以参考前面写过的文章对分层测试的思考
在企业实践中,通常在单元测试阶段对代码覆盖率有较高的期望。这是因为从技术角度来说,单元测试能够最大限度地利用打桩技术来提升覆盖率。相反,如果你试图在集成测试或前端页面测试阶段将代码覆盖率提升到一定程度,你将面临巨大的挑战,甚至在很多情况下,这可能是无法实现的。
代码覆盖率的类型
- 语句覆盖
语句覆盖是指已经被执行到的语句占总可执行语句的百分比,是最基本的覆盖率类型,它要求每个可执行的语句至少被执行一次。这种类型的覆盖率可以帮助我们发现语句级别的错误,但是它无法发现控制流中的错误。
- 分支覆盖
用以度量程序中每一个判定的分支是否都被测试到了,即代码中每个判断的取真分支和取假分支是否各被覆盖至少各一次。这种类型的覆盖率可以帮助我们发现分支点的错误,但是它无法发现复杂的控制流中的错误。
- 条件覆盖
条件覆盖是指,判定中的每个条件的可能取值至少满足一次,衡量判定中的每个条件的结果 TRUE 和 FALSE 是否都被测试到了。这种类型的覆盖率可以帮助我们发现条件错误,但是它无法发现复杂的控制流中的错误。
- 路径覆盖
路径覆盖要求每个可能的执行路径都至少被执行一次。这是最强的覆盖率类型,它可以帮助我们发现所有的控制流错误。但是,由于可能的执行路径的数量可能非常大,所以在实际中往往很难达到完全的路径覆盖。
代码覆盖率的不足之处
我们先来思考一个问题,如果我们通过各种办法和努力,已经把某个方法的MC/DC代码覆盖率做到了 100%,软件质量是否就真的 100% 不会出现错了呢?
但是很可惜,即便我们设计的测试用例已经达到 100% 的代码覆盖率,软件产品的质量也做不到100% 没有问题。其根本原因在于代码覆盖率的计算是基于已经存在的代码的,并不能发现那些开发遗漏或者未开发需求的代码形成的缺陷。
我给你说一个实际的案例,你就明白了,我们一个结算系统里面,原来是 C# 写的,后面是有Java语言重构了一次,大部分功能都没问题,开发测试一起用了半年时间完成上线了,当时单元测试覆盖率达到了恐怖的 98%,集成测试阶段代码覆盖率也达到 90% 以上,大家都认为上线之后问题不多,但是发现上线一个星期之后,部分B端用户反馈说批量下载一个打包部分资源失败(一种售卖模式),提示无权限?最后排查是开发代码漏掉了这个逻辑,没进行开发。
代码覆盖率主要反映了已有代码中,哪些逻辑已被执行,哪些尚未执行。这为我们提供了补充测试用例的依据,使我们能够测试那些未被覆盖的执行路径。然而,这只是其功能的一部分。对于那些尚未实现的代码部分,代码覆盖率的统计指标则无法提供帮助。
简而言之,高的代码覆盖率并不能完全保证软件的质量,但低的代码覆盖率肯定无法确保软件的质量。
代码覆盖率工具
对于每个语言都有自己对应的代码覆盖率统计工具,下面是一些常见的覆盖率工具:
- JaCoCo:JaCoCo 是一款 Java 代码的主流开源覆盖率工具,可以很方便地嵌入到 Ant、Maven 中,并且和很多主流的持续集成工具以及代码静态检查工具,比如 Jekins 和 Sonar 等,都有很好的集成。官网地址:jacoco链接
- goc:goc 是专为 Go 语言打造的一个综合覆盖率收集系统,尤其适合复杂的测试场景,比如系统测试时的代码覆盖率收集以及精准测试。官网地址:goc链接
- go test -cover:Go语言自带的简单覆盖率工具,易于使用,适合单元测试快速获取覆盖率数据
- istanbul/JSCover: 统计JavaScript程序代码覆盖率的工具。官网地址:istanbul链接 jscoverage链接
- **XcodeCoverage:**XcodeCoverage是通过gcc编译的时候加入gcov统计代码覆盖率,Gcov可以执行函数覆盖、语句覆盖和分支覆盖,是一个 Objective-C 语言的覆盖率统计工具。官网地址:XcodeCoverage链接
- **PHP_CodeCoverage:**PHP_CodeCoverage是一个通用的测试覆盖率工具,它可以与各种PHP框架和测试工具配合使用。它还可以生成多种类型的测试覆盖率报告,包括HTML、XML、CSV等格式。官网地址:PHPCodeCoverage链接
- Coverage:Coverage.py是一个用于统计Python程序代码覆盖率的工具,官网地址:Coverage官网地址
- GCOV/lcov:GCOV是一个测试C/C++代码覆盖率的工具,使用它可以看出哪些代码被执行了,被执行的次数和时间。lcov用来输出成 html格式的报告。
代码覆盖率工具的实现原理
我们这里面以JaCoCo为例来说明**。**
JaCoCo 的详细报告,体现出来了代码覆盖率工具的强大。我们有没有仔细想过,这样的统计信息如何被获取到的呢?
实现代码覆盖率的统计,最基本的方法就是注入(Instrumentation)。简单地说,注入就是在被测代码中自动插入用于覆盖率统计的探针代码,并保证插入的探针代码不会给原代码带来任何影响。
JaCoCo支持几种不同的方法来收集覆盖信息,对于每种方法,由不同技术实现的,下图橙色路径部分是JaCoCo 推荐使用的方式,即通过On-The-Fly方式收集覆盖率信息:
通过上图我们知道,JaCoCo 是通过对Java字节码(Byte Code)插入探针的方式来收集覆盖率信息的,探针是可以插入现有指令之间的附加指令。它们不会改变方法的行为,但会记录它们已被执行的事实。
下图是JaCoCo 的整体代码覆盖率统计报告,包括了每个 Java 代码文件的行覆盖率以及分支覆盖率统计,并给出了每个 Java 代码文件的行数、方法数和类数等具体信息。
下图是每个 Java 文件内部详细的代码覆盖率情况,图中绿色的行表示已经被覆盖,红色的行表示尚未被覆盖,黄色的行表示部分覆盖;左侧绿色菱形块表示该分支已经被完全覆盖、黄色菱形块表示该分支仅被部分覆盖。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
了解详情》docs.qq.com/doc/DSlVlZExWQ0FRSE9H