代码覆盖率工具JaCoCo——实践篇

发表于:2018-5-11 17:16

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:刘洋    来源:51testing软件测试网采编

  一、覆盖率项目中使用介绍
  本节开始详细介绍下项目中的JaCoCo实战经验。
  下图是覆盖率在实际在项目中的主要实施点:   
  分别详细介绍下:
  1.1 确定插桩方式
  Android项目只能使用JaCoCo的离线插桩方式。
  为什么?主要是因为Android覆盖率的特殊性:
  一般运行在服务器java程序的插桩可以在加载class文件进行,运用java Agent的机制,可以理解成”实时插桩”。JaCoCo提供了自己的Agent,完成插桩的同时,还提供了丰富的dump输出机制,如File,Tcp Server,Tcp Client。覆盖率信息可以通过文件或是Tcp的形式输出。这样外部程序可很方便随时拿到被测程序的覆盖率。
  但是Android系统破坏了JaCoCo这种便利性,原因有两个:
  (1)Android虚拟机不同与服务器上的JVM,它所支持的字节码必须经过处理支持Android Dalvik等专用虚拟机,所以插桩必须在处理之前完成,即离线插桩模式。
  (2)Android虚拟机没有配置JVM 配置项的机制,所以应用启动时没有机会直接配置dump输出方式。
  1.2 分析项目打包流程
  项目目前还是已build方式打包,属于Apache Ant方式。
  插桩前先熟悉下项目build内容。
  项目主要有几个build文件:
  存放在根目录下的build.xml文件,这个是项目构建的组织文件
  ant目录下的build_common.xml,这个是构建时target内容
  ant目录下的build_option.xml,定义的属性文件。
  ant目录下的build_plugins.xml,插件文件。
  在插桩前,应该对项目构建过程做一个总体的熟悉,了解下每个target的作用,这样才能确定不会影响各个插桩点,不会遗漏,否则会在打包的过程中出现各种各样的问题。
  1.3 代码插桩
  http://eclemma.org/jacoco/trunk/doc/ant.html,这个地址是JaCoCo的ant的说明文档。
  里面简单介绍了其支持的task类型,包括: 
  Task coverage、Task agent、Task merge、Task report、Task instrument、Task dump
  具体怎么使用可以参考里面的例子。
  各Task实际调用的类,看一下JaCoCo的antlib.xml就知道了。  

  项目根据自己的情况暂时只用到了Task instrument,其他dump、merge、report是通过其他方式使用的,具体后面有说明。
  为什么没有用到dump、merge、report?
  这种情况比较适合一个带有自动化测试的构建:打包、自动化测试、dump、merge、report。
  项目部分功能需要手工测试,因此,上述几个步骤需要后面再另外处理。 
  OK,简单了解了JaCoCo的ant方式,下面开始对项目进行插桩打包。
  项目的插桩修改步骤:
  主要修改了build-common.xml和build-plugins.xml两个文件: 
  以下是build-common.xml的修改,build-plugins.xml的修改就不累述了,原理一样。
  (1)文件开头的命名空间加入 
  xmlns:JaCoCo="antlib:org.JaCoCo.ant"
  (2)引入 JaCoCo 的 jar 和相关定义
  `<taskdef uri="antlib:org.JaCoCo.ant" resource="org/JaCoCo/ant/antlib.xml">
  <classpath path="${basedir}/libs/JaCoCoant.jar" />`
  (3)重新定义 class 文件生成路径 
  <property name="classes_instr" value="${temp}/classes_instr" />
  (4)修改compile编译节点,插桩注入 
  `
  <fileset dir="${classes}" includes="**/*.class" />
  </JaCoCo:instrument>`
  (5)修改打包package节点,主要是指定 JaCoCo 编译后的类路径 
  <jar basedir="${classes_instr}" destfile="temp.jar" />
  (6)修改混淆obfuscate节点,增加混淆所需要的 
  <arg value="-libraryjars ${lib}/JaCoCoagent.jar" /> 
  将delete、mkdir、unzip操作指向classes_instr
  (7)修改分包splitClasses节点,指向classes_instr 
  <arg value="${classes_instr}" />
  (8)修改热补丁注入injectPatchCode节点,指向classes_instr 
  <YYBInjectPatchCode inputDir="${classes_instr}"
  (9)修改dex节点,指向classes_instr
   <arg path="${classes_instr}"
  (10)修改dex-sub节点,指向classes-instr,同时在excludes中加入jacocoagent.jar 
  <arg path="${classes_instr}" 
  fileset dir="${lib}" excludes="tmdownloadsdk.jar,tmapkpatch.jar,.....,jacocoagent.jar" />
  将上面的操作,做成全自动修改,打包成autoinsertxml.jar,放到打包服务器后台指定的目录下。
  Jar包里详细内容如下: 
  ●修改AndroidManifest.xml文件,增加一个覆盖率生成服务(这个后续的覆盖率生成工具用到)
  ●修改build_common.xml文件,实现主干代码插桩修改
  ●修改build_plugins.xml文件,实现插件代码的插桩修改
  1.4 打覆盖率包
  Jekin上已经配置好了jacoco_package任务 
  按描述输入后,直接点击开始构建就行了,打包后的结果:
  包括:未插桩的主干类文件、未插桩的插件类文件、三种方式的覆盖率包、mapping文件等等。    
  jacoco_package任务里面的具体内容做了什么?一起看看吧。
  (1)配置了参数化构建的内容,如 
  (2)配置了构建描述   
 
  (3)配置了项目ID和创建精准入库任务
 
  (4)Check out代码 
  (5)插桩    
  (6)编译打包 
   
  (7)备份class  
  
  (8)保存存档文件 
  1.5 执行测试,收集覆盖率结果文件
  覆盖率文件生成现在支持两种方式:
  1、覆盖率生成工具:一个专门用来生成覆盖率文件的APK。
  2、定时器的方式:在项目里新建一个定时器JOB任务,定时去收集生成覆盖率文件。
  目前我们主要用第一种方式,下面都详细介绍下。
  1.5.1 AndroidManifest文件的修改
  增加了两个服务:
  ResultManagerService:执行生成覆盖率数据。
  ReSetManagerService:执行清理覆盖率数据。 
  1.5.2 生成覆盖率的apk工具和jacoco-cov-sdk.jar包
  工具总共有三个功能:
  (1)生成ec文件
  (2)启动定时器,按指定的时间生成ec文件
  (3)清除覆盖率,会清除内存记录并且会删除sd卡存在的ec文件
  工具原理:
  (1)生成ec文件
  当触发这个操作的时候,其实会去启动项目中我们添加的ResultManagerService服务,它具体做的事情就是dump覆盖率数据,如下:在ResultManagerService启动时调用jacoco-cov-sdk.jar包中的ResultManager.dumpCoverageJacoco(true,filename)方法:
  其主要功能就是反射调用jaCoCo的dump方法,来生成覆盖率数据,核心代码如下: 
  `//Get AgentOptions class 
  Class classAgentOptions = Class.forName(“org.jacoco.agent.rt.internal_b0d6a23.core.runtime.AgentOptions”);
  //Get setDestfile method in AgentOptions class
  Method methodSetDestFile = classAgentOptions.getMethod("setDestfile",String.class);
  //Get FileOutput class
  Class classFileOutput = Class.forName("org.jacoco.agent.rt.internal_b0d6a23.output.FileOutput");
  //Get field "File destFile" in FileOutput class
  Field fieldFile = classFileOutput.getDeclaredField("destFile");
  fieldFile.setAccessible(true);      
  //Get Agent singleton by getAgent method in RT class
  Class<?> RT = Class.forName("org.jacoco.agent.rt.RT");
  Method methodGetAgent = RT.getMethod("getAgent");
  Object objAgent = methodGetAgent.invoke(null);      
  //Get Agent Class
  Class classAgent = Class.forName("org.jacoco.agent.rt.internal_b0d6a23.Agent");
  //Get field "AgentOptions options" and "FileOutput output" in Agent Class
  Field fieldOptions = classAgent.getDeclaredField("options");
  Field fieldOutput = classAgent.getDeclaredField("output");
  fieldOptions.setAccessible(true);
   fieldOutput.setAccessible(true);
  //Get options/output object referenced by Agent singleton
  Object objOptions = fieldOptions.get(objAgent);
  Object objOutput = fieldOutput.get(objAgent);
  //change destFile attribute in options object by setDestfile method
  methodSetDestFile.invoke(objOptions,absFilePath);
  //change field "File destFile" in output object
  File destFile = new File(absFilePath).getAbsoluteFile();
  fieldFile.set(objOutput,destFile);
  //dump
  Method methodDump = classAgent.getMethod
  ("dump",boolean.class);
  methodDump.invoke(objAgent,reset);
`
  (2)启动定时器,按指定的时间生成ec文件
  这个就是一个Timer,按指定的时间周期去dump覆盖率数据
  (3)清除覆盖率,会清除内存记录并且会删除sd卡存在的ec文件
  当触发这个操作的时候,其实会去启动项目中我们添加的ReSetManagerService服务,它具体做的事情就是reset覆盖率数据,如下:
  在ReSetManagerService启动时调用jacoco-cov-sdk.jar包中的ResultManager.reSetCoverageJacoco()方法:
  其主要功能就是反射调用jaCoCo的reset方法,来清理覆盖率数据,核心代码如下:
  `Class<?> RT = Class.forName("org.jacoco.agent.rt.RT");
  Method methodGetAgent = RT.getMethod("getAgent");
  Object objAgent = methodGetAgent.invoke(null);      
  //Get Agent Class
  Class classAgent = Class.forName("org.jacoco.agent.rt.internal_b0d6a23.Agent");
  //reset
  Method methodDump = classAgent.getMethod("reset");
  methodDump.invoke(objAgent,null);


上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号