1.本地跑maven项目集成jacoco获取单元测试代码覆盖率,在pom.xml的build标签里面增加该插件
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.8</version>
<executions>
<!--这里的execution ,每一个执行的goal,对应的id必须是唯一的 -->
<!-- prepare-agen这个就相当于插桩,发生在default生命周期的initialize阶段 -->
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<!--这个check:对代码进行检测,控制项目构建成功还是失败 -->
<!-- <execution>
<id>check</id>
<goals>
<goal>check</goal>
</goals>
</execution> -->
<!--这个report:对代码进行检测,然后生成index.html在 target/site/index.html中可以查看检测的详细结果 -->
<!-- report这个就是生成代码覆盖率报告,发生在default生命周期的verify阶段 -->
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<!-- <execution> <id>post-unit-test</id> <phase>test</phase> <goals>
<goal>report</goal> </goals> <configuration> <dataFile>target/jacoco.exec</dataFile>
<outputDirectory>target/jacoco-ut</outputDirectory> </configuration> </execution> -->
</executions>
<!-- <configuration> <systemPropertyVariables> <jacoco-agent.destfile>target/jacoco.exec</jacoco-agent.destfile>
</systemPropertyVariables> </configuration> -->
<!-- Configuration 里面写配置信息 -->
<configuration>
<!-- rules里面指定覆盖规则 -->
<rules>
<rule implementation="org.jacoco.maven.RuleConfiguration">
<element>BUNDLE</element>
<limits>
<!-- 指定方法覆盖到80% -->
<limit implementation="org.jacoco.report.check.Limit">
<counter>METHOD</counter>
<value>COVEREDRATIO</value>
<minimum>0.80</minimum>
</limit>
<!-- 指定指令覆盖到80% -->
<limit implementation="org.jacoco.report.check.Limit">
<counter>INSTRUCTION</counter>
<value>COVEREDRATIO</value>
<minimum>0.80</minimum>
</limit>
<!-- 指定行覆盖到80% -->
<limit implementation="org.jacoco.report.check.Limit">
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.80</minimum>
</limit>
<!-- 指定类覆盖到100%,不能遗失任何类 -->
<limit implementation="org.jacoco.report.check.Limit">
<counter>CLASS</counter>
<value>MISSEDCOUNT</value>
<maximum>0</maximum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</plugin>
2.服务器跑jacoco获取部署的服务代码覆盖率
2.1、获取代码覆盖率文件几种方式
(1)output=file 获取覆盖率文件必须要停掉服务,才能获取
(2)output=tcpserver
不需要停服务,直接可以运行中获取代码覆盖率文件,tcpserver这种方式可以作为一个服务器一样,供客户端远程调用ip和制定代理的端口,获取覆盖率exec文件
(3)output=tcpclient
不需要停服务,直接可以运行中获取代码覆盖率文件,tcpclient这种方式作为客户端,不断向我们自己部署的代码覆盖率收集服务器发送覆盖率数据,生成覆盖率ex
ec文件
(4)output=none不输出任何覆盖率文件
2.2、我这里使用output=tcpserver的方式来获取覆盖率文件,操作如下:
(1)启动服务
我跑war项目,tomcat服务器增加jvm配置:我是在tomcat目下的bin下的catalina.sh增加的配置
# -javaagent: 的后面跟jacoco的安装路径
# includes= 选项,选择你要覆盖率的服务
# port= 选项,选择你要打开的端口
# address= 选项,tomcat服务所在机器的ip地址(如果想在跟tomcat服务同一台机器上执行ant任务的话,需要改为127.0.0.1或者*)
JAVA_OPTS="-javaagent:/path/to/your/jacoco_0.6.4/lib/jacocoagent.jar=includes=*,output=tcpserver,port=8893,address=*"
如果是jar包,需要直接启动的时候添加JAVA_OPTS参数,例如启动一个springboot项目
例如:
java -javaagent:/usr/local/jacoco/lib/jacocoagent.jar=includes=*,output=tcpserver,port=8045,address=* -jar hello-0.0.1-SNAPSHOT.jar --server.port=9999
(2)查看是否生效
ps -ef |grep tomcat
看到有jacocoagent的配置生效即可
网上说如果发现启动主程序异常,那么在JAVA_OPTS 配置中国再加一项-Xverify:none,有问题可以试下
(3)远程dump出覆盖率exec文件
方法1:通过ant来执行操作,获取覆盖率exec文件
在项目目录下加入build.xml文件,直接ant ,多个子项目时可以多加几个配置
例如:
<?xml version="1.0" ?>
<project name="hello" xmlns:jacoco="antlib:org.jacoco.ant" default="jacocoReport">
<property name="srcPath" value="src/main/java"/>
<property name="classPath" value="target/classes"/>
<!--Jacoco ant的安装路径-->
<property name="jacocoantPath" value="C:\Program Files\jacoco-0.8.2\lib\jacocoant.jar"/>
<property name="jacocoexecPath" value="./merged.exec"/>
<property name="workspacePath" value="."/>
<!--生成覆盖率报告的路径-->
<property name="reportfolderPath" value="./report"/>
<!--远程tomcat服务的ip地址-->
<property name="server_ip" value="47.99.206.219"/>
<!--前面配置的远程tomcat服务打开的端口,要跟上面配置的一样-->
<property name="server_port" value="9999"/>
<taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml">
<classpath path="${jacocoantPath}" />
</taskdef>
<target name="dump">
<jacoco:dump address="${server_ip}" reset="false" destfile="${jacocoexecPath}" port="${server_port}" append="true"/>
</target>
<target name="merge" depends="dump" >
<jacoco:merge destfile="merged.exec">
<fileset dir="${workspacePath}" includes="**/*.exec"/>
</jacoco:merge>
</target>
<target name="jacocoReport" depends="merge">
<delete dir="${reportfolderPath}" />
<mkdir dir="${reportfolderPath}" />
<jacoco:report>
<executiondata>
<file file="${jacocoexecPath}" />
</executiondata>
<structure name="JaCoCo Report">
<group name="testgroupname">
<classfiles>
<fileset dir="${classPath}" />
</classfiles>
<sourcefiles encoding="utf-8">
<fileset dir="${srcPath}" />
</sourcefiles>
</group>
</structure>
<html destdir="${reportfolderPath}" encoding="utf-8" />
</jacoco:report>
</target>
</project>
方法2:通过使用jacocoAPI来发送dump请求,接受返回的覆盖率exec文件
下面给出一个例子:
package com.kang.jacoco;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import org.jacoco.core.data.ExecutionDataWriter;
import org.jacoco.core.runtime.RemoteControlReader;
import org.jacoco.core.runtime.RemoteControlWriter;
import org.junit.Test;
import com.kang.jacoco.example.ReportGenerator;
/**
* 测试jacoco
* IP:47.99.206.219
* port:8044
* @author 67639
*
*/
public class JacocoTest {
//jacoco覆盖率文件本地生成的地址
private static final String JACOCOEXEC_DESTFILE = "C:\\Users\\67639\\git\\springboot\\eshop-cache-ha\\jacoco.exec";
//生成报告的地址
private static final String REPORT_DIR = "C:\\Users\\67639\\git\\springboot\\eshop-cache-ha";
private static final String ADDRESS = "47.99.206.219";
private static final int PORT = 9999;
public static void main(String[] args) throws IOException {
JacocoTest jacocotest =new JacocoTest();
jacocotest.test01();
jacocotest.test02();
}
public void test02() throws IOException {
ReportGenerator report =new ReportGenerator(new File(REPORT_DIR));
report.create();
}
public void test01() throws IOException {
final FileOutputStream localFile = new FileOutputStream(JACOCOEXEC_DESTFILE);
final ExecutionDataWriter localWriter = new ExecutionDataWriter(localFile);
// Open a socket to the coverage agent:
final Socket socket = new Socket(InetAddress.getByName(ADDRESS), PORT);
final RemoteControlWriter writer = new RemoteControlWriter(
socket.getOutputStream());
final RemoteControlReader reader = new RemoteControlReader(
socket.getInputStream());
reader.setSessionInfoVisitor(localWriter);
reader.setExecutionDataVisitor(localWriter);
// Send a dump command and read the response:
writer.visitDumpCommand(true, false);
System.out.println("发送dump请求");
if (!reader.read()) {
throw new IOException("Socket closed unexpectedly.");
}
socket.close();
localFile.close();
}
}
3.到报告的目录下打开index.html即可查看覆盖率报告