导读
高德地图开放平台产品不断迭代,代码逻辑越来越复杂,现有的测试流程不能保证完全覆盖所有业务代码,测试不到的代码及分支,会存在一定的风险 。为了保证测试全面覆盖,需要引入代码覆盖率做为测试指标,需要对SDK代码进行染色,测试结束后可生成代码覆盖率报告,作为发版前的一项重要卡点指标 。本文小结了Android端代码染色原理及技术实践 。
JaCoCo工具
JaCoCo有以下优点:
- 支持Ant和Gradle打包方式,可以自由切换 。
- 支持离线模式,更贴合SDK的使用场景 。
- JaCoCo文档比较全面,还在持续维护,有问题便于解决 。
Jacoco探针
由于Java字节码是线性的指令序列,所以JaCoCo主要是利用ASM处理字节码,在需要的地方插入一些特殊代码 。
我们通过Test1方法观察一下JaCoCo做的处理 。
//原始java方法public static int Test1(int a, int b) {int c = a + b;int d = c + a;return d;}//--------------------------我是分割线--------------------------------------------////jacoco处理后的方法private static transient /* synthetic */ boolean[] $jacocoData;public static int Test1(final int a, final int b) {final boolean[] $jacocoInit = $jacocoInit();final int c = a + b;final int n;final int d = n = c + a;$jacocoInit[3] = true;return n;}private staticboolean[] $jacocoInit() {boolean[] $jacocoData;if (($jacocoData = https://www.isolves.com/it/cxkf/ydd/Android/2020-09-16/TestInstrument.$jacocoData) == null) {$jacocoData = (TestInstrument.$jacocoData =Offline.getProbes(-6846167369868599525L,"com/jacoco/test/TestInstrument", 4));}return $jacocoData;}
【Android端代码染色原理及技术实践】可以看出代码中插入了多个Boolean数组赋值,自动添加了jacocoInit方法和jacocoData数组声明 。
JaCoCo统计覆盖率就是标记Boolean数组, 只要执行过的代码,就对相应角标的Boolean数组进行赋值, 最后对Boolean进行统计即可得出覆盖率,这个数组官方的名字叫探针 (Probe) 。
探针是由以下四行字节码组成,探针不改变该代码的行为,只记录他们是否已被执行,从理论上讲,可以在每行代码都插入一个探针,但是探针本身需要多个字节码指令,这将增加几倍的类文件的大小和执行速度,所以JaCoCo有一定的插桩策略 。
ALOADprobearrayxPUSHprobeidICONST_1BASTORE
探针插桩策略探针的插入需要遵循一定策略,大体可分成以下三个策略:
- 统计方法的执行情况 。
- 统计分支语句的执行情况 。
- 统计普通代码块的执行情况 。
这个比较容易处理, 在方法头或者方法尾加就可以了 。
- 方法尾加: 能说明方法被执行过, 且说明了探针上面的方法被执行了,但是这种处理比较麻烦, 可能有多个return或者throw 。
- 方法头加: 处理简单, 但只能说明方法有进去过 。
public void visitInsn(final int opcode) {switch (opcode) {case Opcodes.IRETURN:case Opcodes.LRETURN:case Opcodes.FRETURN:case Opcodes.DRETURN:case Opcodes.ARETURN:case Opcodes.RETURN:case Opcodes.ATHROW:probesVisitor.visitInsnWithProbe(opcode, idGenerator.nextId());break;default:probesVisitor.visitInsn(opcode);break;}}
分支的执行情况
Java字节码通过Jump指令来控制跳转,分为有条件Jump和无条件Jump 。
- 无条件Jump (goto)
官方文档中介绍
![Android端代码染色原理及技术实践](http://img.jiangsulong.com/220422/02314H143-0.jpg)
文章插图
示例代码
![Android端代码染色原理及技术实践](http://img.jiangsulong.com/220422/02314J319-1.jpg)
文章插图
有条件Jump (if-else)
这种经常出现于if等有条件的跳转语句,JaCoCo会对if语句进行反转,将字节码变成if not的逻辑结构 。
为什么要对if进行反转?下面示例将说明原因 。
Test4方法是一个普通的单条件if语句,可以看到JaCoCo将>10的条件反转成<=10,为什么要进行反转而不是直接在原有if后面增加else块呢?继续往下看复杂一点的情况 。
//源码public static void Test4(int a) {if(a>10){a=a+10;}a=a+12;}?//jacoco处理后的字节码public static void Test4(int a) {boolean[] var1 = $jacocoInit();if (a <= 10) {var1[11] = true;} else {a += 10;var1[12] = true;}a += 12;var1[13] = true;}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ueditor 百度编辑器的代码块显示功能的设置
- 你的接口参数怎么接收的
- JetPack现在都成了Android开发必备技能嘛?
- 我用过的几款SSH客户端工具
- VueJS中使用前端虚拟接口Mock.js
- 如何使用VSCode调试JS?
- 避免开源代码漏洞的4个最佳实践
- Nignx的安装和使用
- Vuex 映射完全指南
- EditPlus——一款小巧功能强大的老牌代码文本编辑器