环境信息
ant 下载并配置到环境变量
本地安装 Jenkins 服务,拉取代码并打包 (不需要部署)
Jacoco 下载并解压到目录 (例如直接放到 D 盘)
build.xml

<?xml version="1.0" encoding="UTF-8"?>

<!--
   Copyright (c) 2009, 2020 Mountainminds GmbH & Co. KG and Contributors
   This program and the accompanying materials are made available under
   the terms of the Eclipse Public License 2.0 which is available at
   http://www.eclipse.org/legal/epl-2.0

   SPDX-License-Identifier: EPL-2.0

   Contributors:
      Marc R. Hoffmann - initial API and implementation
-->

<project name="Example Ant Build with JaCoCo" default="rebuild" xmlns:jacoco="antlib:org.jacoco.ant">

    <description>
      Example Ant build file that demonstrates how a JaCoCo coverage report
      can be itegrated into an existing build in three simple steps.
    </description>

    <!--Jacoco的安装路径-->
<property name="jacocoantPath" value="D:/jacoco-0.8.6/lib/jacocoant.jar"/>
<!--最终生成.exec文件的路径,Jacoco就是根据这个文件生成最终的报告的-->
<property name="jacocoexecPath" value="D:/jacoco_result_html/jacoco-client.exec"/>
<!--生成覆盖率报告的路径-->
<property name="reportfolderPath" value="D:/jacoco_result_html/report"/>
<!--远程tomcat服务的ip地址-->
<property name="server_ip" value="10.130.35.112"/>
<!--前面配置的远程tomcat服务打开的端口,要跟上面配置的一样-->
<property name="server_port" value="8855"/>
<property name="homedir" value="C:/Users/Admin/.jenkins/workspace/springproject"/>
<!--源代码路径-->
<!--可以配置多个源代码-->
<property name="checkOrderSrcpath" value="${homedir}/src/main/java/"/>

<!--.class文件路径-->
<!--跑的是class,标注的是源码?-->
<property name="checkOrderClasspath" value="${homedir}/target/classes" />



<!--让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}" port="${server_port}" append="true"/>

</target>
<target name="merge">   
 <jacoco:merge destfile="${jacocoExecPath}/merged.exec">      
    <fileset dir="${jacocoExecPath}" includes="*.exec"/>    
 </jacoco:merge>

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

        </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" />      


    </jacoco:report>

</target>

</project>

build.xml 里面的 checkOrderSrcpath 和 checkOrderClasspath 属性需要指定到本地 jenkins 编译项目的路径,文件放到任意目录 (例如:D:\jacoco_result),打开命令行跳转到该目录,执行 ant dump

再执行 ant report

查看报告

查看 exec 文件信息:

代码不改动的情况下获取覆盖率没有问题,实际在测试中开发会提交多次代码,是否会影响代码覆盖率?我们来试一下

修改代码 eat() 方法,add1() 方法增加 2 行,重新部署,postman 执行 add2() 方法,然后执行 ant dump(前面的 exec 文件重命名一下,后面会用到) 和 ant report

查看 exec 文件信息:

build.xml dump 命令 append="true”,会把 2 次的结果追加到一起,但是对于 com/example/spring/http/HttpServer 这个类,2 次的 classID 不同并没有把覆盖率结果合并到一起,报告只是第二次执行的覆盖率,从 jacoco 源码 (https://github.com/jacoco/jacoco.git) 也能看出来

如果想要合并 2 次的覆盖率则需要修改源码.

同一个类的测试覆盖率合并
jacoco 默认覆盖率合并是根据 classID 来的,相同的 classID 才会合并,需要调整一下策略支持根据类名来合并覆盖率。

org.jacoco.core.data.ExecutionDataStore
定义了 Map 类型的变量 entries,key 是 classID.把它改成 Map类型,key 为类名;下面的方法:put(),subtract(),get() 等也相应的修改下.
org.jacoco.core.data.ExecutionData#assertCompatibility
把 id 相等的判断去掉

org.jacoco.core 项目执行:mvn clean package -Dmaven.test.skip=true ,引用打出的 jar 包执行 merge 操作 即可把 2 次的 exec 文件合并
差异行覆盖率
目的:只看测试分支 (test) 和基准分支 (master) 有差异的代码的覆盖率
思路:
1.获取差异的代码--JGit
2.生成报告的时候只生成差异代码的覆盖率
代码参考:https://github.com/512433465/JacocoPlus.git

org.jacoco.core.analysis.CoverageBuilder
引入差异的代码 list public static List classInfos;
org.jacoco.core.internal.flow.ClassProbesAdapter#visitMethod 只统计差异的覆盖率

private boolean isContainsMethod(String currentMethod,
List classInfos) {
if (classInfos == null || classInfos.isEmpty()) {
return true;
}
String currentClassName = name.replaceAll("/", ".");
for (ClassInfo classInfo : classInfos) {
String className = classInfo.getPackages() + "."
+ classInfo.getClassName();
if (currentClassName.equals(className)) {
for (MethodInfo methodInfo : classInfo.getMethodInfos()) {
String methodName = methodInfo.getMethodName();
if (currentMethod.equals(methodName)) {
return true;
}
}
}
}
return false;
}
org.jacoco.report.internal.html.page.SourceHighlighter#renderCodeLine
设置报告的颜色

org.jacoco.core 项目执行:mvn clean package -Dmaven.test.skip=true ,引用打出的 jar 包执行 report 操作


↙↙↙阅读原文可查看相关链接,并与作者交流