痛点
常见场景
场景一:
开发哥:“刚修复了一个紧急用户反馈,安排测试测一下吧!”
测试姐:“改了哪些地方?对哪些功能有影响?”
开发哥:“改了好些地方,为了保险,把基本功能都测一下吧。”
场景二:
开发哥:“昨天的修改测试完成了吗?”
测试姐:“哥,别催!还在测试中,要跑500个用例呢!”
开发哥:“啊,我只修改了一行代码吧,怎么需要测这么多呢?”
场景三:
TL:“我们目前测试覆盖率多高?”
QA:“啊!这。。。”
这是我们测试过程中常遇到的3个场景:
场景一不管是开发和测试,都很难明确的给出此次代码修改的影响范围,我们更多凭借着经验判断去猜测这次代码修改可能影响了 某些模块,而这种凭借经验的是存在非常大的不确定性,不可控。
场景二随着被测系统越来越复杂,往往动一发而牵全身,测试不得不花大量的时间去做回归测试,而又因为无法获取到明确的影响范围,测试往往做着全量的回归测试。
场景三当老板问起我们的测试覆盖率时,我们很难拿出一个明确的数据。 有人可能讲,我的覆盖率是居于需求点的,每个需求点我都有用例覆盖,所以我的覆盖率是100%,有的同学可能会居于用例库的用例,用例库里面有1000条用例,我这次执行了900条,所以我的测试覆盖率是90%,然而不管是哪一种这里面是无法精准量化,例如1000条用例真实有效是几条? 100个功能点是怎么去划分出这100个功能点的?
测试过程
传统软件测试过程中,有计划(P),有执行(D)但这过程中主要通过人力检查,依赖人的经验等最后造成质量抖动,并没有能精准的对过程跟踪,对结果无法精准量化,无法形成统一标准推广。
以上的场景和测试过程其实都是反应了目前我们测试的3个痛点:
- 模糊、猜测:对测试范围的猜测
- 效率低
- 过程和结果都无法精准量化
破卷之路 - 精准测试
什么是精准测试
百度百科:精准测试是一套计算机测试辅助分析系统。精准测试的核心组件包含的软件测试示波器、用例和代码的双向追溯、智能回归测试用例选取、覆盖率分析、缺陷定位、测试用例聚类分析、测试用例自动生成系统,这些功能完整的构成了精准测试技术体系。
简单说精准测试是一套技术体系,在这个体系中包含了如用例和代码的双向追溯、智能回归测试用例选取、覆盖率分析等等,而我们今天就主要介绍代码覆盖率统计。
什么是代码覆盖率
百度百科:代码覆盖(英语:Code coverage)是软件测试中的一种度量,描述程序中源代码被测试的比例和程度,所得这个比例称为代码覆盖率。
简单说就是用于度量被测代码的比例和程度。
代码覆盖率统计方式
我们目前主要看的是行覆盖率和分支覆盖率,这里面就只介绍这两种。
- 行覆盖:顾名思义代码行数的覆盖。
- 分支覆盖:是指代码if else while等有条件导致的分支时,分支的覆盖情况。
看个例子:
if(a>3){
log.info(a);
}else{
log.err("err");
}
以上代码行数是5, 分支数是2(a>3 要嘛true ,要嘛false)
if(a>3 && b<10){
log.info(a);
}else{
log.err("err");
}
以上代码行数同样是5, 分支数是4 (a>3 是true或者false , b<10 也是true 或者false ,他们就有4种组合就是4个分支)
Jacoco
而我们想做代码覆盖率统计的话,不同端用不同的方案,不同的开发语言也会有对应不同的框架,而我们目前大部分项目的业务逻辑是在后端,并且基本是java,所以我们选用了jacoco 这个框架。
什么是Jacoco
官方:Jacoco是一个免费的Java代码覆盖库。
为什么选Jacoco
- 免费
- 社区活跃
- 支持运行时插桩
- 获取报告不需要停服务
- 等等
市面上类似的框架有很多个,为啥选Jacoco,综合来说jacoco这几个优点是我选择它的主要原因,当然最重要的是它可以在不需要开发修改源码的情况下,通过Java Agent技术变能达到报告的采集等。
Jacoco的工作原理
Jacoco使用插桩的方式来记录覆盖率数据,是通过一个probe探针来注入。如上案例,Jacoco 在 b()方法调用后、c()方法以及d()方法调用后插入了探针,每个探针其实就是一个 boolean 值,当程序执行到时, boolean 值就为true ,没执行到就为false, 最后通过收集到这些boolean来判定哪些代码已经被执行,哪些代码未被执行。
可能这会你会好奇,jacoco 为啥会在那些位置插入探针而不是别的位置。 首先插入探针的规则是Jacoco自带的,详细规则可以自行百度查看。 关于上个图,我这边简单画个道路图,你把探针当做是一个个摄像头,试想下你想知道从入口进去,出口出来的话,你的监控得放哪些地方技能监控到走过的路,又能减少摄像头数量(前提摄像头不能识别方向,只能识别有没有从这里经过)。
两种插桩模式
Jacoco 的有两种插桩模式:
- 编译时插桩:offline模式
- 运行时插桩:on-the-fly模式
编译时插桩,就是源码在被编译的过程就对文件进行插桩,生成插过桩的class或jar包,然后测试时使用插过桩的class和jar包,生成覆盖率信息到文件,最后统一处理,生成报告,常见的开发单元测试代码覆盖率报告就是这种方式。
运行时插桩,其实就是在应用部署的时候,让jacoco的相关工具(jacocoagent)介入到部署过程,相关工具在加载class的时候,动态改变字节码结构,插入jacoco的探针。而探针不改变原有方法的行为,只是记录是否已经执行。也就是我们选用的方案,它不需要开发额外修改代码,也基本(这里用基本,主要我们遇到过一个坑后面会介绍)不会改变原有的方法行为。
报告生成三步曲
如果我们选用了Jacoco作为代码覆盖率收集的框架,又选用了运行时插桩模式,那么我们想获取到报告大概只需要经历三个步骤:
对被测项目进行字节码插桩(javaagent)
java -javaagent:path/jacocoagent.jar=includes=*,output=tcpserver,port=9527,address=0.0.0.0,append=true -jar xxxxx.jar
覆盖率数据的采集与导出(dump)
java -jar jacococli.jar dump --address xxx.xxx.xx.xx --port 9527 --destfile ./xxxx.exec
覆盖率数据的统计与报告(report)生成
java -jar jacoco-cli.jar report xxx.exec \ --sourcefiles path/xxxx \ --classfiles path/xxx \ --html ./xxx \ --encoding=utf8
报告解读
报告类型
- 全量报告:当前部署分支/Tag代码的完整覆盖率
- 增量报告:当前部署分支/Tag代码和另一个分支diff后变更代码的覆盖率
- 核心报告:核心类的覆盖率统计,核心功能回归
Html报告解读
红色:没有覆盖
黄色:部分覆盖
绿色:完全覆盖
无背景色:仅会在增量报告中展示,表示当前类的当前方法未变动,同时增量覆盖率统计时,也不会加入统计
过程闭环
- 测试分析:通过覆盖率和差异化测试分析得到测试范围集合
- 测试用例集:根据测试范围设计测试用例
- 执行测试:通过手工或自动化执行测试用例
- 代码覆盖率:工具自动收集相关代码覆盖率报告
- 覆盖率分析:分析覆盖率情况,反推用例设计是否充分、合理,不充分则回到第一步
- 完成测试:测试充分,完成测试
收益预期
缺陷发现率
传统的测试大约70%的缺陷很容易发现,但之后缺陷的发现效率会急剧的下降。而传统的白盒测试技术直接面对代码测试,难度大、效率低,仅关注覆盖率,无系统性。
精准测试是采用传统黑盒测试与白盒测试相结合的模式,它可以在黑盒测试过程中,通过自动采集白盒级别的运行逻辑数据。根据可视化出来的遗漏点,引导开发和测试有针对性的补充测试用例,提高缺陷发现效率,保障测试质量。
覆盖率统计
统计纬度 | 统计方法 | 统计结果 | |
---|---|---|---|
传统方式 | 基于需求点(功能点) 基于用例库 |
人根据经验,主观统计 | 说服力低,准确性差 |
精准测试 | 全量覆盖率 增量覆盖率 核心代码覆盖率 |
程序自动精准统计行覆盖率、分支覆盖率,方法覆盖率等 | 高可信,高精度 |
其它
- 降低了测试人员深度覆盖逻辑的难度,降低漏测率
- 快速且容易验证测试过程的有效性,减少冗余用例
- 发现冗余/无效代码
- 作为提测、上线的度量指标之一
- 等等
平台开发
整体介绍
在无需开发改造项目的情况下,动态的对字节码进行插桩,并收集相应数据,从而能够真实的反馈代码被测试的程度。
在不改变QA原有的测试方法下,平台自动的完成测试覆盖率统计,对未覆盖的代码提醒补充用例覆盖,并以Code Diff的方式给予展示。
流程图
详细介绍
这个篇幅我就略过了,我们一样是站在巨人的肩膀上,根据自己公司和团队情况做了二开。
二开参考了滴滴的 supuer-jacoco 和 Dray 的jacoco
踩过的坑
- 源码和编译后的字节码或部署和使用的字节码不一致
直接拿部署的字节码
- 报告生成速度慢
增量从gitlab拉取代码外,别的都直接从编译打包过程生成获取
- 多模块仅仅统计了部署的模块
项目全量class和源码获取,用户根据实际情况自己配置依赖模块
- Jacocoagent引用的第三方包和开发使用的包冲突
maven-shade-plugin 插件对依赖包重命名
待解决问题
- 同一个分支多次部署,一个类改动,整个类的覆盖率清零
- class注入时会多个 $jacocoData 变量,使用反序列化时会报错,该问题后续需要让服务应用可以定制化自己的启动参数,目前全公司就一份统一的启动脚本
未来计划
把精准测试-独孤九剑一招一式练完,目前基本完成前3式,下个计划做第九式。