环境信息
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 操作