跳至主要內容

Jacoco

程序员李某某大约 5 分钟

Jacoco

原理

Jacoco是一个开源的覆盖率统计工具,它针对的开发语言是java,其使用方法很灵活,可以嵌入到

Ant、Maven中;可以作为Eclipse、IDEA插件,可以使用其JavaAgent技术监控Java程序

很多第三方的工具提供了对Jacoco的集成,如sonar、Jenkins等

原理:Jacoco使用ASM技术修改字节码方法,可以修改Jar文件、class文件字节码文件。

ASM是一个Java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM可以直接产生二进制class文件,也可以在类被加载入Java虚拟机之前动态改变类行为。Java class被存储在严格格式定义的.class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及Java 字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类

插桩方式

  • Offline插桩

    在测试前先对Class文件进行插桩(探针),然后生成插过桩的class或jar包(目标文件),执行目标文件以后得到覆盖执行结果,最终生成覆盖率报告,Offline插桩方式主要用于单测,集成测试等静态场景

  • On The Fly插桩

    java启动时添加 -javaagent 参数指定特定的jar文件启动代理程序,代理程序再通过自定义classloader实现自己的类装载策略,在类加载之前将探针插入class文件中

    更加方便的获取代码覆盖率,无需提前进行字节码插桩,可以实时获取代码覆盖率信息

    主要用于服务化系统的代码动态覆盖率获取

    JaCoCo代理收集执行信息并根据请求或在JVM退出时将其转储。有三种不同的执行数据输出模式:

    • 文件系统:在JVM终止时,执行数据被写入本地文件。

    • TCP套接字服务器:外部工具可以连接到JVM,并通过套接字连接检索执行数据。可以在VM退出时进行可选的执行数据重置和执行数据转储。

    • TCP套接字客户端:启动时,JaCoCo代理连接到给定的TCP端点。执行数据根据请求写入套接字连接。可以在VM退出时进行可选的执行数据重置和执行数据转储

  • 比较

    On-the-fly模式更方便简单进行代码覆盖分析,无需提前进行字节码插桩,无需考虑classpath 的设置,存在如下情况不适合on-the-fly,需要采用offline提前对字节码插桩:

    (1) 运行环境不支持java agent

    (2) 部署环境不允许设置JVM参数

    (3) 字节码需要被转换成其他的虚拟机如Android Dalvik VM

    (4) 动态修改字节码过程中和其他agent冲突

    (5) 无法自定义用户加载类

参数说明

java -javaagent:target/classes/jacocoagent.jar=includes=**/controller/**,output=tcpserver,append=true,address=localhost,port=6200 -jar target/jacoco-code-coverage-0.0.1-SNAPSHOT.jar
  • includes --- 包含的待测类、包
  • excludes --- 排除的类、包
  • output --- 输出方式
    • file --- 停止服务后本地生成
    • tcpserver --- 以服务的形式生成
    • tcpclient
  • append --- true则覆盖已有的exec文件
  • destfile --- exec文件位置,默认为当前目录jacoco.exec
  • address --- 绑定tcpserver 时的ip
  • port --- 绑定tcpserver 时的端口,默认6300
  • classdumpdir --- 相对于代理程序看到的所有类文件转储到的工作目录的位置。这对于调试目的或在动态创建的类(例如使用脚本引擎时)非常有用

mvn集成

  • file --- mvn插件
<plugins>
    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    <!--设置测试失败忽略-->
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <configuration>
            <skipTests>false</skipTests>
            <testFailureIgnore>true</testFailureIgnore>
        </configuration>
    </plugin>

    <!-- 需要在存放测试类模块(通常是web模块)下的pom.xml中,并且在<build>标签下加入以下插件配置:jacoco maven 插件配置 -->
    <plugin>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <version>0.8.8</version>
        <configuration>
            <!--这里配置需要扫描的路径,可使用正则-->
            <includes>
                <include>**/controller/**</include>
            </includes>
            <excludes>
                <!-- 这里配置我们想要排除的包路径 -->
                <!--                        <exclude>com/rcplatform/athena/shorturl/models/*</exclude>-->
                <!-- 通过通配符来排除指定格式的类 -->
                <!-- <exclude>**/*ShortUrl.class</exclude>-->
                <!-- <exclude>**/*ShortUrlVisit.class</exclude>-->
            </excludes>
        </configuration>

        <executions>
            <!--配置覆盖率报告汇总,执行maven:test模块后进行汇总报告数据-->
            <execution>
                <id>prepare-agent</id>
                <goals>
                    <goal>prepare-agent</goal>
                </goals>
            </execution>
            <execution>
                <id>report</id>
                <phase>prepare-package</phase>
                <goals>
                    <goal>report</goal>
                </goals>
            </execution>

            <!--配置覆盖率报告汇总,执行maven:test模块后进行汇总报告数据-->
            <execution>
                <id>post-unit-test</id>
                <phase>test</phase>
                <goals>
                    <!--如果是多模块项目,以下配置表示生成的报表示不按项目模块进行划分,报表明细-->
                    <goal>report</goal>
                    <!--如果是多模块项目,以下配置表示生成的报表示可以按项目模块进行划分,即报表汇总-->
                    <goal>report-aggregate</goal>
                </goals>
                <configuration>
                    <dataFile>target/jacoco.exec</dataFile>
                    <outputDirctory>target/jacoco.exec</outputDirctory>
                </configuration>
            </execution>
        </executions>
    </plugin>
</plugins>

tcpserver脚本

mkdir -p /export/home/jacoco
cd /export/home/jacoco && wget "http://storage.jd.local/bpp-quality-public/code_coverage_statistics/jacoco-0.8.7-20210115.151120-42.tar.gz"
tar -xzvf /export/home/jacoco/jacoco-0.8.7-20210115.151120-42.tar.gz
ip=$(/sbin/ip a | grep "inet " |grep -v "127.0.0.1" | awk '{print $2}' | awk -F/ '{print $1}')
export JAVA_OPTS="-javaagent:/export/home/jacoco/jacoco-0.8.7-20210115.151120-42/lib/jacocoagent.jar=includes=*,output=tcpserver,append=true,port=6200,address='${ip}'"
cd 工程目录

java -jar /export/home/jacoco/jacoco-0.8.7-20210115.151120-42/lib/jacococli.jar dump --address ${ip} --port 6200 --destfile target/jacoco-demo.exec

java -jar /export/home/jacoco/jacoco-0.8.7-20210115.151120-42/lib/jacococli.jar report target/jacoco-demo.exec --classfiles target/classes/com  --sourcefiles src/main/java --html target/report --xml target/report.xml

参考文档

https://www.cnblogs.com/liuyitan/p/15716027.html

https://blog.csdn.net/Michaelwubo/article/details/121142621

https://www.jianshu.com/p/98a17fa6b853

上次编辑于:
贡献者: ext.liyuanhao3