导读
高德地图开放平台产品不断迭代,代码逻辑越来越复杂,现有的测试流程不能保证完全覆盖所有业务代码,测试不到的代码及分支,会存在一定的风险。为了保证测试全面覆盖,需要引入代码覆盖率做为测试指标,需要对SDK代码进行染色,测试结束后可生成代码覆盖率报告,作为发版前的一项重要卡点指标。本文小结了Android端代码染色原理及技术实践。
相关阅读:iOS代码染色原理及技术实践
JaCoCo工具
JaCoCo有以下优点:
支持Ant和Gradle打包方式,可以自由切换。
支持离线模式,更贴合SDK的使用场景。
JaCoCo文档比较全面,还在持续维护,有问题便于解决。
JaCoCo主要是通过ASM技术对Java字节码进行处理和插桩,ASM和Java字节码技术不是本文重点,感兴趣的朋友可以自行了解。下面重点介绍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 static boolean[] $jacocoInit() {
boolean[] $jacocoData;
if (($jacocoData = TestInstrument.$jacocoData) == null) {
$jacocoData = (TestInstrument.$jacocoData =
Offline.getProbes(-6846167369868599525L,
"com/jacoco/test/TestInstrument", 4));
}
return $jacocoData;
}
可以看出代码中插入了多个Boolean数组赋值,自动添加了jacocoInit方法和jacocoData数组声明。
JaCoCo统计覆盖率就是标记Boolean数组, 只要执行过的代码,就对相应角标的Boolean数组进行赋值, 最后对Boolean进行统计即可得出覆盖率,这个数组官方的名字叫探针 (Probe)。
探针是由以下四行字节码组成,探针不改变该代码的行为,只记录他们是否已被执行,从理论上讲,可以在每行代码都插入一个探针,但是探针本身需要多个字节码指令,这将增加几倍的类文件的大小和执行速度,所以JaCoCo有一定的插桩策略。
ALOAD probearray
xPUSH probeid
ICONST_1
BASTORE
探针插桩策略
探针的插入需要遵循一定策略,大体可分成以下三个策略:
统计方法的执行情况。
统计分支语句的执行情况。
统计普通代码块的执行情况。
方法的执行情况
这个比较容易处理, 在方法头或者方法尾加就可以了。
方法尾加: 能说明方法被执行过, 且说明了探针上面的方法被执行了,但是这种处理比较麻烦, 可能有多个return或者throw。
方法头加: 处理简单, 但只能说明方法有进去过。
通过分析源码,发现JaCoCo是在方法结尾处插入探针,retrun和throw之后都会加入探针。
public void visitInsn(final int opcode) {
switch (opcode) {
case Opcodes.IRET