Jacoco获取集成测试覆盖率

一、Jacoco简介

Jacoco是专门用来统计单元测试覆盖率集成测试覆盖率的十分常用的工具。

二、Jacoco插桩

主流代码覆盖率工具都采用字节码插桩模式,通过钩子的方式来记录代码执行轨迹信息。其中字节码插桩又分为编译时插桩运行时插桩,分别对应Offline模式On-the-fly模式 。On-The-Fly模式优点在于无需修改源代码,可以在系统不停机的情况下,实时收集代码覆盖率信息。Offine模式优点在于系统启动不需要额外开启代理,但是只能在系统停机的情况下才能获取代码覆盖率

On-The-Fly插桩:Java Agent
(1)JVM中通过 -javaagent 参数,指定特定的jar文件启动Instrumentation的代理程序
(2)代理程序在每装载一个class文件前,判断是否已经转换修改了该文件,如果没有则需要将探针插入class文件中
(3)代码覆盖率就可以在JVM执行代码的时候实时获取
(4)典型代表:Jacoco

On-The-Fly插桩:Class Loader
(1)自定义classloader实现自己的类装载策略,在类加载之前将探针插入class文件中
(2)典型代表:Emma

Offine插桩
(1)在测试之前先对文件进行插桩,生成插过桩的class文件或者jar包。执行插过桩的class文件或者jar包之后,会生成覆盖率信息到文件。最后统一对覆盖率信息进行处理,并生成报告。
(2)Offline插桩又分为两种:

   1》Replace:修改字节码生成新的class文件
   2》Inject:在原有字节码文件上进行修改

(3)典型代表:Cobertura

On-The-Fly和Offine比较
(1)On-The-Fly模式更加方便的获取代码覆盖率,无需提前进行字节码插桩,可以实时获取代码覆盖率信息
(2)Offline模式适用于以下场景:

      运行环境不支持java agent
      部署环境不允许设置JVM参数
      字节码需要被转换成其他虚拟机字节码,如Android Dalvik VM
      动态修改字节码过程中和其他agent冲突
      无法自定义用户加载类
2.1 插桩前准备工作
  1. 下载jacoco.jar,官网地址https://www.eclemma.org/jacoco/
  2. 将jacoco.jar解压,cd到jacoco项目的lib目录下。jacocoagent.jar 就是启动应用时主要用来插桩的jar包
    在这里插入图片描述
    ⚠️:请注意不要写错名称,里面有个很像的jacocoant.jar,这个jar包是用ant xml方式操作jacoco时使用的,不要混淆
2.2 不同部署方式实现插桩

包括:Ant插件启动、Maven插件启动、java -jar启动、tomcat启动war包

方式一:java -jar启动

java -javaagent:/Users/sundongping/Downloads/jacoco-0.8.5/lib/jacocoagent.jar=includes=*,output=tcpserver,port=2014,address=127.0.0.1 -jar ./target/jacocotest-1.0-SNAPSHOT.jar

后台启动需要使用 nohup … & 命令

nohup java -javaagent:/Users/sundongping/Downloads/jacoco-0.8.5/lib/jacocoagent.jar=includes=*,output=tcpserver,port=2014,address=127.0.0.1 -jar ./target/jacocotestmaven-1.0-SNAPSHOT.jar&
  • javaagent
    jdk5之后新增的参数,主要用来在运行jar包的时候,以一种方式介入字节码加载过程,如有兴趣自行百度。注意后面有个冒号:

  • /Users/sundongping/Downloads/jacoco-0.8.5/lib/jacocoagent.jar
    需要用来介入class文件加载过程的jar包。是jacocoagent.jar包的绝对路径

  • includes=*
    代表启动时需要进行字节码插桩的包的过滤,*代表所有的class文件加载都需要进行插桩。假如你们公司内部代码都有相同的包缀:com.mycompany
    你可以写成:includes=com.mycompany.*

  • output=tcpserver
    支持file、tcpserver、tcpclient等参数值

  • port=2014
    这是jacoco开启的tcpserver的端口,请注意这个端口不能被占用

  • address=127.0.0.1
    这是对外开放的tcpserver的访问地址。可以配置127.0.0.1,也可以配置为实际访问ip。配置为127.0.0.1的时候,dump数据只能在这台服务器上进行dump,就不能通过远程方式dump数据。配置为实际的ip地址的时候,就可以在任意一台机器上(前提是ip要通,不通都白瞎),通过ant xml或者api方式dump数据。

    举个栗子:
    我如果配置了192.168.110.1:2014作为jacoco的tcpserver启动服务,那么可以在任意一台机器上进行数据的dump,比如在我本机mac上用api或者xml方式调用dump;

    如果配置了127.0.0.1:2014作为启动服务器,那么只能在这台测试机上进行dump,其他的机器都无法连接到这个tcpserver进行dump

    总结:
    格式是固定的,只有括号内的东西方可改变,其它尽量不要动,连空格都不要多

-javaagent:(/home/admin/jacoco/jacocoagent.jar)=includes=(*),output=tcpserver,port=(2014),address=(127.0.0.1)

方式二:外部tomcat启动war包

修改项目的pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>jacocotesttomcat</groupId>
    <artifactId>jacocotesttomcat</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!-- packaging是打包标签,默认是打成jar包,打war包需要将标签值改成war-->
    <packaging>war</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!-- 不使用内嵌tomcat的方式一:移除嵌入式tomcat插件 springboot内嵌tomcat服务器,所以当要使用外部的tomcat时需要先去除内置tomcat-->
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- 不使用内嵌tomcat的方式二:<scope>provided</scope>表示在编译和测试时使用(不加它,打的包中会指定tomcat,用tomcat部署时会因tomcat版本报错;而加上它,打包时不会把内置的tomcat打进去)-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

        <!--使用jacoco对web工程生成全部的覆盖率报告-->
        <dependency>
            <groupId>org.jacoco</groupId>
            <artifactId>org.jacoco.core</artifactId>
            <version>0.8.5</version>
        </dependency>

        <dependency>
            <groupId>org.jacoco</groupId>
            <artifactId>org.jacoco.report</artifactId>
            <version>0.8.5</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

在服务器tomcat安装路径的bin目录下,找到catalina.sh。打开catalina.sh,找到合适的地方修改JAVA_OPTS参数
在这里插入图片描述
将项目war包放到tomcat的webapps目录下(tomcat会自动将war包解压)
启动tomcat sudo sh ./startup.sh,使用命令 ps aux|grep tomcat查看tomcat进程。若进程信息中包含javaagent的内容表示插桩成功
在这里插入图片描述
方式三:Maven插件启动

maven项目启动的命令

mvn clean install
mvn tomcat7:run -Dport=xxx

mvn clean install
mvn spring-boot:run -Dport=xxx

这两套命令,本质上没什么差别,只是运行插件不一样。在当前代码的pom文件层级运行,意思是通过maven的tomcat插件启动这个服务。这个服务启动在端口xxxx上,注意这个端口是应用的访问端口,和jacoco的那个端口不是一回事

maven实现jacoco插桩的命令

export MAVEN_OPTS="-javaagent:$jacocoJarPath=includes=*,output=tcpserver,port=2014,address=127.0.0.1"

这句命令加在哪里呢?就是run之前。因为这样一改,你的所有的mvn命令都会生效,但其实我们只想介入启动过程

因此,前面提到的两套启动命令,就可以改成如下方式:

mvn clean install
export MAVEN_OPTS="-javaagent:$jacocoJarPath=includes=*,output=tcpserver,port=2014,address=127.0.0.1"
mvn tomcat7:run -Dport=xxx
export MAVEN_OPTS=""

mvn clean install
export MAVEN_OPTS="-javaagent:$jacocoJarPath=includes=*,output=tcpserver,port=2014,address=127.0.0.1"
mvn spring-boot:run -Dport=xxx
export MAVEN_OPTS=""

最后修改为"",是因为担心对后续的mvn命令产生影响。其实如果你切换了terminal窗口,这个临时变量就会失效,不会对环境造成污染。如果应用启动成功了,可以使用netstat判别一下tcp服务是否真的启动

方式四:Ant插件启动

配置build.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project name="Jacoco" xmlns:jacoco="antlib:org.jacoco.ant" default="jacoco">
    <property name="jacocoantPath" value="/Users/sundongping/Downloads/jacoco-0.8.5/lib/jacocoant.jar"/>
    <property name="integrationJacocoexecPath" value="./jacoco-integration.exec"/>
    <property name="reportfolderPath" value="/opt/app/mskyprocess/jacoco/file/jacocoReport/"/>
    <property name="checkOrderSrcpath" value="/Users/sundongping/IdeaProjects/jacocotestmaven/dirtest/src/main/java" />
    <property name="checkOrderClasspath" value="/Users/sundongping/IdeaProjects/jacocotestmaven/dirtest/target/classes" />
    <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml">
      <classpath path="${jacocoantPath}" />
    </taskdef>

    <target name="dump">
        <jacoco:dump address="127.0.0.1" port="2014" reset="true" destfile="${integrationJacocoexecPath}" append="false"/>
    </target>

    <target name="clean">
       <!-- 清空文件 -->
       <delete dir="/opt/app/mskyprocess/jacoco/file/jacocoReport/it_coverage" />
    </target>

    <target name="report">
        <jacoco:report>
            <executiondata>
                <file file="${integrationJacocoexecPath}" />
            </executiondata>
            <structure name="JaCoCo Report">
                <group name="Check qaportal 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(很多注释掉的生成报告的路径,是不用jenkins的时候可以本地生产html使用的)

<?xml version="1.0" encoding="UTF-8"?>
<project name="Jacoco" default="jacoco" xmlns:jacoco="antlib:org.jacoco.ant">
<!--Jacoco的安装路径-->
<property name="jacocoantPath" value="/opt/app/ant/apache-ant-1.10.5/jacocoant.jar"/>
<!-- <property name="integrationJacocoexecPath" value="./jacoco-integration.exec"/>-->
<!--最终生成.exec文件的路径,Jacoco就是根据这个文件生成最终的报告的-->
<property name="jacocoexecPath" value="/opt/app/jenkins/workspace/Jacoco_umegateway02/gateway/target/jacoco.exec"/>
<!--生成覆盖率报告的路径-->
<!-- <property name="reportfolderPath" value="/opt/app/apache-ant-1.10.5/coverage_ant_task/report/"/>-->
<!--远程tomcat服务的ip地址-->
<property name="server_ip" value="10.221.159.8"/>
<!--前面配置的远程tomcat服务打开的端口,要跟上面配置的一样-->
<property name="server_port" value="8044"/>
<!--源代码路径-->
<!-- <property name="checkOrderSrcpath" value="/data/Ume/umegateway/gateway/src/main/java" />-->
<!--.class文件路径-->
<!-- <property name="checkOrderClasspath" value="/data/Ume/umegateway/gateway/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>
<!--jacoco任务:
根据前面配置的源代码路径和.class文件路径,
根据dump后,生成的.exec文件,生成最终的html覆盖率报告。-->
<target name="report">
<!-- <delete dir="${reportfolderPath}" />-->
<!-- <mkdir dir="${reportfolderPath}" />-->
<jacoco:report>
<executiondata>
<file file="${jacocoexecPath}" />
</executiondata>
<!-- <structure name="JaCoCo Report">-->
<!-- <group name="Check qaportal 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>

参考博文:
关于Jacoco的小结和踩坑记录
Jacoco Code Coverage
JaCoCo在Tomcat服务器上监控代码覆盖率的使用方法
https://www.cnblogs.com/dingtian/p/7754079.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值