痛点

常见场景

场景一:
开发哥:“刚修复了一个紧急用户反馈,安排测试测一下吧!”
测试姐:“改了哪些地方?对哪些功能有影响?”
开发哥:“改了好些地方,为了保险,把基本功能都测一下吧。”

场景二:
开发哥:“昨天的修改测试完成了吗?”
测试姐:“哥,别催!还在测试中,要跑500个用例呢!”
开发哥:“啊,我只修改了一行代码吧,怎么需要测这么多呢?”

场景三:
TL:“我们目前测试覆盖率多高?”
QA:“啊!这。。。”

这是我们测试过程中常遇到的3个场景:
场景一不管是开发和测试,都很难明确的给出此次代码修改的影响范围,我们更多凭借着经验判断去猜测这次代码修改可能影响了 某些模块,而这种凭借经验的是存在非常大的不确定性,不可控。
场景二随着被测系统越来越复杂,往往动一发而牵全身,测试不得不花大量的时间去做回归测试,而又因为无法获取到明确的影响范围,测试往往做着全量的回归测试。
场景三当老板问起我们的测试覆盖率时,我们很难拿出一个明确的数据。 有人可能讲,我的覆盖率是居于需求点的,每个需求点我都有用例覆盖,所以我的覆盖率是100%,有的同学可能会居于用例库的用例,用例库里面有1000条用例,我这次执行了900条,所以我的测试覆盖率是90%,然而不管是哪一种这里面是无法精准量化,例如1000条用例真实有效是几条? 100个功能点是怎么去划分出这100个功能点的?

测试过程

image.png
传统软件测试过程中,有计划(P),有执行(D)但这过程中主要通过人力检查,依赖人的经验等最后造成质量抖动,并没有能精准的对过程跟踪,对结果无法精准量化,无法形成统一标准推广。

以上的场景和测试过程其实都是反应了目前我们测试的3个痛点:

  • 模糊、猜测:对测试范围的猜测
  • 效率低
  • 过程和结果都无法精准量化

    破卷之路 - 精准测试

    什么是精准测试

    百度百科:精准测试是一套计算机测试辅助分析系统。精准测试的核心组件包含的软件测试示波器、用例和代码的双向追溯、智能回归测试用例选取、覆盖率分析、缺陷定位、测试用例聚类分析、测试用例自动生成系统,这些功能完整的构成了精准测试技术体系

简单说精准测试是一套技术体系,在这个体系中包含了如用例和代码的双向追溯、智能回归测试用例选取、覆盖率分析等等,而我们今天就主要介绍代码覆盖率统计。

什么是代码覆盖率

百度百科:代码覆盖(英语:Code coverage)是软件测试中的一种度量,描述程序中源代码被测试的比例和程度,所得这个比例称为代码覆盖率。

简单说就是用于度量被测代码的比例和程度。

代码覆盖率统计方式

我们目前主要看的是行覆盖率和分支覆盖率,这里面就只介绍这两种。

  • 行覆盖:顾名思义代码行数的覆盖。
  • 分支覆盖:是指代码if else while等有条件导致的分支时,分支的覆盖情况。

看个例子:

  1. if(a>3){
  2. log.info(a);
  3. }else{
  4. log.err("err");
  5. }

以上代码行数是5, 分支数是2(a>3 要嘛true ,要嘛false)

  1. if(a>3 && b<10){
  2. log.info(a);
  3. }else{
  4. log.err("err");
  5. }

以上代码行数同样是5, 分支数是4 (a>3 是true或者false , b<10 也是true 或者false ,他们就有4种组合就是4个分支)

Jacoco

而我们想做代码覆盖率统计的话,不同端用不同的方案,不同的开发语言也会有对应不同的框架,而我们目前大部分项目的业务逻辑是在后端,并且基本是java,所以我们选用了jacoco 这个框架。

什么是Jacoco

官方:Jacoco是一个免费的Java代码覆盖库。

为什么选Jacoco

  • 免费
  • 社区活跃
  • 支持运行时插桩
  • 获取报告不需要停服务
  • 等等

市面上类似的框架有很多个,为啥选Jacoco,综合来说jacoco这几个优点是我选择它的主要原因,当然最重要的是它可以在不需要开发修改源码的情况下,通过Java Agent技术变能达到报告的采集等。

Jacoco的工作原理

image.png
Jacoco使用插桩的方式来记录覆盖率数据,是通过一个probe探针来注入。如上案例,Jacoco 在 b()方法调用后、c()方法以及d()方法调用后插入了探针,每个探针其实就是一个 boolean 值,当程序执行到时, boolean 值就为true ,没执行到就为false, 最后通过收集到这些boolean来判定哪些代码已经被执行,哪些代码未被执行。
可能这会你会好奇,jacoco 为啥会在那些位置插入探针而不是别的位置。 首先插入探针的规则是Jacoco自带的,详细规则可以自行百度查看。 关于上个图,我这边简单画个道路图,你把探针当做是一个个摄像头,试想下你想知道从入口进去,出口出来的话,你的监控得放哪些地方技能监控到走过的路,又能减少摄像头数量(前提摄像头不能识别方向,只能识别有没有从这里经过)。
image.png

两种插桩模式

Jacoco 的有两种插桩模式:

  • 编译时插桩:offline模式
  • 运行时插桩:on-the-fly模式

编译时插桩,就是源码在被编译的过程就对文件进行插桩,生成插过桩的class或jar包,然后测试时使用插过桩的class和jar包,生成覆盖率信息到文件,最后统一处理,生成报告,常见的开发单元测试代码覆盖率报告就是这种方式。

运行时插桩,其实就是在应用部署的时候,让jacoco的相关工具(jacocoagent)介入到部署过程,相关工具在加载class的时候,动态改变字节码结构,插入jacoco的探针。而探针不改变原有方法的行为,只是记录是否已经执行。也就是我们选用的方案,它不需要开发额外修改代码,也基本(这里用基本,主要我们遇到过一个坑后面会介绍)不会改变原有的方法行为。

报告生成三步曲

如果我们选用了Jacoco作为代码覆盖率收集的框架,又选用了运行时插桩模式,那么我们想获取到报告大概只需要经历三个步骤:

  1. 对被测项目进行字节码插桩(javaagent)

    1. java -javaagent:path/jacocoagent.jar=includes=*,output=tcpserver,port=9527,address=0.0.0.0,append=true -jar xxxxx.jar
  2. 覆盖率数据的采集与导出(dump)

    java -jar jacococli.jar dump --address xxx.xxx.xx.xx --port 9527 --destfile ./xxxx.exec
    
  3. 覆盖率数据的统计与报告(report)生成

    java -jar jacoco-cli.jar report xxx.exec \
    --sourcefiles path/xxxx \
    --classfiles path/xxx \
    --html ./xxx \
    --encoding=utf8
    

报告解读

报告类型

  • 全量报告:当前部署分支/Tag代码的完整覆盖率
  • 增量报告:当前部署分支/Tag代码和另一个分支diff后变更代码的覆盖率
  • 核心报告:核心类的覆盖率统计,核心功能回归

Html报告解读

image.png
红色:没有覆盖
黄色:部分覆盖
绿色:完全覆盖
无背景色:仅会在增量报告中展示,表示当前类的当前方法未变动,同时增量覆盖率统计时,也不会加入统计

过程闭环

image.png

  1. 测试分析:通过覆盖率和差异化测试分析得到测试范围集合
  2. 测试用例集:根据测试范围设计测试用例
  3. 执行测试:通过手工或自动化执行测试用例
  4. 代码覆盖率:工具自动收集相关代码覆盖率报告
  5. 覆盖率分析:分析覆盖率情况,反推用例设计是否充分、合理,不充分则回到第一步
  6. 完成测试:测试充分,完成测试

收益预期

缺陷发现率

image.png
传统的测试大约70%的缺陷很容易发现,但之后缺陷的发现效率会急剧的下降。而传统的白盒测试技术直接面对代码测试,难度大、效率低,仅关注覆盖率,无系统性。
精准测试是采用传统黑盒测试与白盒测试相结合的模式,它可以在黑盒测试过程中,通过自动采集白盒级别的运行逻辑数据。根据可视化出来的遗漏点,引导开发和测试有针对性的补充测试用例,提高缺陷发现效率,保障测试质量。

覆盖率统计

image.png

统计纬度 统计方法 统计结果
传统方式 基于需求点(功能点)
基于用例库
人根据经验,主观统计 说服力低,准确性差
精准测试 全量覆盖率
增量覆盖率
核心代码覆盖率
程序自动精准统计行覆盖率、分支覆盖率,方法覆盖率等 高可信,高精度

其它

  • 降低了测试人员深度覆盖逻辑的难度,降低漏测率
  • 快速且容易验证测试过程的有效性,减少冗余用例
  • 发现冗余/无效代码
  • 作为提测、上线的度量指标之一
  • 等等

平台开发

整体介绍

image.png

  1. 在无需开发改造项目的情况下,动态的对字节码进行插桩,并收集相应数据,从而能够真实的反馈代码被测试的程度。

  2. 在不改变QA原有的测试方法下,平台自动的完成测试覆盖率统计,对未覆盖的代码提醒补充用例覆盖,并以Code Diff的方式给予展示。

流程图

image.png

详细介绍

这个篇幅我就略过了,我们一样是站在巨人的肩膀上,根据自己公司和团队情况做了二开。
二开参考了滴滴的 supuer-jacoco 和 Dray 的jacoco

踩过的坑

  • 源码和编译后的字节码或部署和使用的字节码不一致

直接拿部署的字节码

  • 报告生成速度慢

增量从gitlab拉取代码外,别的都直接从编译打包过程生成获取

  • 多模块仅仅统计了部署的模块

项目全量class和源码获取,用户根据实际情况自己配置依赖模块

  • Jacocoagent引用的第三方包和开发使用的包冲突

maven-shade-plugin 插件对依赖包重命名

待解决问题

  • 同一个分支多次部署,一个类改动,整个类的覆盖率清零
  • class注入时会多个 $jacocoData 变量,使用反序列化时会报错,该问题后续需要让服务应用可以定制化自己的启动参数,目前全公司就一份统一的启动脚本

未来计划

精准测试-独孤九剑一招一式练完,目前基本完成前3式,下个计划做第九式。

平台其它模块

用例管理
造数中心
接口管理(待分享)