通用技术 Java 覆盖率相关实践总结

小敏 · 2018年03月02日 · 最后由 cc 回复于 2020年02月25日 · 483 次阅读

最近接手的新项目需要使用代码覆盖率进行辅助测试,所以结合之前项目的实践笔记及经验,把 Java 代码覆盖率相关的知识做了一个回顾和总结。
我们一般都是用 jacoco+jenkins 进行代码覆盖率的统计和展示。从实践来看,变更代码覆盖率分析更贴合我们日常的使用场景。在版本迭代及回归测试阶段,可以采用黑盒测试 + 变更覆盖分析(白盒测试)的灰盒测试方法,完善测试场景,减少测试遗漏,保证测试的充分性。

具体的 jacoco 的配置及 jenkins 的配置可以参考以下文档:
jenkins+jacoco 配置代码覆盖率
Jacoco Code Coverage

附我们项目中使用的 build.xml 配置:

<?xml version="1.0" ?>
<project name="Example" xmlns:jacoco="antlib:org.jacoco.ant" basedir="." default="rebuild">
    <!--Jacoco文件路径-->
    <property name="jacocoantPath" value="/home/test/tools/jacoco-0.7.4/lib/jacocoant.jar"/>
    <!--最终生成.exec文件的路径,Jacoco根据这个文件生成最终的报告-->
    <property name="jacocoexecPath" value="/home/test/jenkins/workspace/Example-Coverage/jacoco_ci_report/exec/"/>
    <!--生成覆盖率报告的路径-->
    <property name="reportfolderPath" value="/home/test/jenkins/workspace/Example-Coverage/jacoco_ci_report/report/"/>
    <!--远程tomcat服务的ip-->
    <property name="server_ip" value="10.165.***.**"/>
    <!--jacocoagent服务打开的端口-->
    <property name="server_port" value="8035"/>
    <!--源代码路径-->
    <property name="checkOrderSrcpath" value="/home/test/jenkins/workspace/Example-Coverage/moduleName/src/main/java" />
    <!--.class文件路径-->
    <property name="checkOrderClasspath" value="/home/test/jenkins/workspace/Example-Coverage/moduleName/target/classes" />
    <tstamp>  
        <format property="stime" pattern="yyyy-MM-dd HH-mm-ss" locale="cn"/>  
    </tstamp> 
    <!--让ant知道去哪儿找Jacoco-->
    <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml">
        <classpath path="${jacocoantPath}" />
    </taskdef>

    <!--dump任务: 根据前面配置的ip地址和端口,访问目标tomcat服务,并生成.exec文件。-->
    <target name="dump">
        <jacoco:dump address="${server_ip}" reset="false" destfile="${jacocoexecPath}ExampleApiTest${stime}.exec" port="${server_port}" append="true" dump="true"/>
    </target>
    <!--merge指定的.exec文件-->
    <target name="merge_exec">
        <jacoco:merge destfile="${jacocoexecPath}ExampleApiTest-merge.exec">
            <fileset dir="${jacocoexecPath}" includes="ExampleApiTest*.exec" />
        </jacoco:merge>
    </target>

    <!--jacoco任务:根据前面配置的源代码路径和.class文件路径,及merge后生成的.exec文件,生成最终的html覆盖率报告-->
    <target name="report">
        <delete dir="${reportfolderPath}" />
        <mkdir dir="${reportfolderPath}" />
     <jacoco:report>
          <executiondata>
              <file file="${jacocoexecPath}ExampleApiTest-merge.exec" />
          </executiondata>

          <structure name="JaCoCo Report">
              <group name="Check Order related">           
                  <classfiles>
                      <fileset dir="${checkOrderClasspath}" />
                  </classfiles>
                  <sourcefiles encoding="gbk">
                      <fileset dir="${checkOrderSrcpath}" />
                  </sourcefiles>
              </group>
          </structure>

          <html destdir="${reportfolderPath}" encoding="utf-8" />         
          <_xml destfile="${reportfolderPath}/jacoco_report.xml" encoding="utf-8" />
    </jacoco:report>
    </target>
    <target name="rebuild" depends="dump,merge_exec,report"></target>
</project>
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 16 条回复 时间 点赞
陈恒捷 testerhome 新发表的话题页面打不开 中提及了此贴 03月02日 23:56

不错,可以分享下你们的变更代码覆盖率的生成思路吗?全量的用 jacoco 官方的就行,变更代码的难度更高。

陈恒捷 回复

重点是获取变更代码,我们是参考美团的方案做的:https://tech.meituan.com/android-jacoco-practace.html

通过抽象语法树获取到精确到方法级别的变更,然后改造 jacoco 生成 report 的机制,只生成变更代码部分的报告。

AngryTester 回复

我后来发现其实靠 jar 或者 dex 自身也能分析出代码变更

AngryTester 回复

不错。美团的这种方式我们之前也有看过,但局限性是插桩的时候就限定了变更范围,自由度降低了。

我们采用的是结合 git diff 在 jacoco 全量报告的基础上修改报告,列表页只保留增量部分文件和统计增量部分数据,以及代码页增量代码行增加标识的方式。

陈恒捷 回复

😂 我们也只是参考他们的方案
因为我们的应用还是 web 居多 基本都涉及中间件 设置 jvm 参数很方便 所以还是用的 on-the-fly 模式

陈恒捷 回复

我们其实也是这么做的

陈恒捷 回复

是的,就是这个实现原理

陈恒捷 回复

chenhengjie123,请问:以及代码页增量代码行增加标识的方式 这个是怎么实现的呢?

pangpangdelian 回复

原理上就是根据 diff 识别文字和行数,然后修改报告的 html ,在有修改的那一行(一般在 diff 里面是 + 开头)加个 css 样式或者图标。

这个倒不难,难的是怎么让统计数据也是只统计增量的。

陈恒捷 回复

增量报告这块做法和你们是一样的,但是增量代码行增加标识这块的实现还是没有很好的方案,目前做法是:1、解析 diff 文件获取变更。2.基于变更在脚本中(java 或 python 中)动态给变更的 html 添加样式。不过以上方法感觉很笨,想了解下咱们是怎么实现的这块,请指教哈

pangpangdelian 回复

我们也是同样的做法,结合 diff 修改 html ,变成增量的覆盖率报告。毕竟素材就是全量报告,所以也没有更好的解决办法了。

请教一下,关于增量报告这一块。如果我在 includes 里面指定具体的某个类,实际上 dump 下来的二进制文件中也只是收集了这个类的信息。为什么在生成报告的时候,是生成的全量报告呢? 如果我只是想生成这个类的报告。应该怎么做呢 ?

@chenhengjie123 想请教一下 ,“怎么让统计数据也是只统计增量” 的思路,最近也在搞这个,被卡住了。

kell 回复

我了解到的是通过读取生成的全量报告里一个专门存储覆盖率数据的部分,通过解析它来计算的。行覆盖率通过 git diff 获取到的改动后的行号,然后对比覆盖率数据确认是否有覆盖来计算。大致公式是:已覆盖改动行/所有改动行数

没有经手这个项目,很细节的算法不是很了解。

陈恒捷 回复

感谢,我再思考一下。目前思路是根据 diff 出来的结果去 class 目录找出变更集,再根据这个去生成报告。

楼主你好,有个问题想请教下,
1、第一次源码文件有一个方法,测试使其已覆盖,并拉取 exec 数据,生成的报告可以看到相关的代码已被覆盖。
2、编辑 1 中源码文件,新增一个方法,重新编译打包并启动这个项目,进行测试使新增代码覆盖,并拉取 exec 数据,但是这次生成的报告,只看到了新代码被覆盖,以前覆盖的代码(1 中代码)没有被覆盖
我用的是 on-fly 方式,reset 是 false,append 是 true,也就是 exec 文件是有以前的覆盖率数据的。
请问你们公司在实践中有遇到这样的情况吗,是怎么处理的呢?因为现在一个版本测试中会有多次代码更新,每次开发改完 bug 发版,之前测好的代码的覆盖率也没有了。
求指教〜〜

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册