测试覆盖率工具之--02 Jacoco使用

Jacoco 统计的是全量代码覆盖率。它不仅支持生成单元测试的覆盖率,也支持监控生成接口测试,功能测试的覆盖率。

一. 工作原理

1.1. Jacoco 工作原理

  先来看一下 Jacoco 工作原理,如下图所示:
Jacoco 工作原理图

1.2. 工作步骤

  • 对Java字节码进行插桩,有on-the-fly和offline两种方式。
  • 执行测试用例,收集程序执行轨迹信息,支持通过dump将操作记录从服务端传输到本地。
  • 数据处理器结合程序执行轨迹信息和代码结构信息分析生成代码覆盖率报告。
  • 结合源码和编译后的文件,可以将代码覆盖率报告图形化展示出来,如html,xml等文件格式。

二. Jacoco使用

2.1. Ant 项目

原文:
    JaCoCo comes with Ant tasks to launch Java programs with execution recording and for creating coverage reports from the recorded data. Execution data can be collected and managed with the tasks coverage, agent, dump and merge. Reports in different formats are created with the report task. For offline instrumentation the task instrument can be used to prepare class files.

翻译:
  JaCoCo 附带了Ant任务。它通过启动带有记录执行功能的Java程序,来实现生成覆盖率报告。其中任务(coverage, agent, dump and merge)用于收集和管理执行信息;任务 report 用于创建不同格式的报告。任务instrument 用于为 offline 模式 instrumentation 准备类文件。详细说明见
  注意:build.xml中,有特定的 compile 阶段。请务必保证,有 debug="true" 这个配置,否则 jacoco 是无法注入的。有的时候ant项目生成的覆盖率数据大小为0,就可以去排查下这里。
  实例如下:

2.2. Maven 项目

  JaCoCo Maven 插件定义了了如下goals
  1. help
  2. prepare-agent
  3. prepare-agent-integration
  4. merge
  5. report
  6. report-integration
  7. report-aggregate
  8. check
  9. dump
  10. instrument
  11. restore-instrumented-classes
  注意:Export MAVEN_OPTS参数时,后续的所有mvn命令,都会带上此参数,因此相当于每次执行mvn命令,都会尝试启动代理,因此可能会出现address bind already in use之类的异常抛出。因此,我们只有在mvn tomcat7:run启动服务器时才需要启动代理,其他如mvn的编译、install命令都不需要,所以在启动之后,把MAVEN_OPTS参数置空,或者重启一个terminal来执行命令

  具体实例如下:

2.3. Java Agent

  Java Agent 只支持 on-the-fly 模式插桩,即所有类文件在加载到JVM期间独立于应用程序框架进行内存预处理。jacocoagent.jar 是JaCoCo发行版的一部分,可以使用以下JVM选项激活Java代理。详细说明见

-javaagent:[yourpath/]jacocoagent.jar=[option1]=[value1],[option2]=[value2]

JaCoCo代理支持的选项如下:

选项 描述 默认
destfile 执行数据的输出文件的路径。 jacoco.exec
append 如果设置为true并且执行数据文件已经存在,则将覆盖数据附加到现有文件。如果设置为 false,则将替换现有的执行数据文件。 true
includes 执行分析中应包含的类名列表。列表条目以冒号(:)分隔,可以使用通配符(*和?)。除了性能优化或技术角落案例,通常不需要此选项。 * (所有类)
excludes 应从执行分析中排除的类名称列表。列表条目以冒号(:)分隔,可以使用通配符(*和?)。除了性能优化或技术角落案例,通常不需要此选项。 空(不排除类)
exclclassloader 应从执行分析中排除的类加载器名称的列表。列表条目以冒号(:)分隔,可以使用通配符(*和 ?)。如果特殊框架与JaCoCo代码工具发生冲突,特别是无法访问Java运行时类的类加载器,则可能需要此选项。 sun.reflect.DelegatingClassLoader
inclbootstrapclasses 指定是否还应该检测引导类加载器的类。谨慎使用此功能,需要大量包括/不包括调整。 false
inclnolocationclasses 指定是否还应该检测没有源位置的类。通常这样的类是在运行时产生的,例如通过模拟框架,因此在默认情况下被排除。 false
sessionid 与执行数据一起写入的会话标识符。没有这个参数,代理就会创建一个随机的标识符。 自动生成
dumponexit 如果设置为true覆盖数据,将在VM关闭时写入。如果file指定了转储,或者输出为tcpserver/ tcpclient 并且在虚拟机终止时连接处于打开状态,则只能写入转储。 true
output 用于写入覆盖率数据的输出方法。有效的选项是:
file:在虚拟机终止执行数据写入destfile属性中指定的文件。
tcpserver:代理侦听由address和port属性指定的TCP端口上的传入连接。执行数据被写入到这个TCP连接。
tcpclient:启动时,代理将连接到由address和port属性指定的TCP端口。执行数据被写入到这个TCP连接。
none:不要产生任何输出。
请参阅下面的安全考虑。
file
address 当输出方法为tcpserver或连接到 输出方法时要绑定的IP地址或主机名 tcpclient。在tcpserver模式中,值“ *”使代理接受任何本地地址上的连接。 回环接口
port 当输出方法是绑定的端口,tcpserver或者当输出方法是连接的端口tcpclient。在 tcpserver模式下,端口必须可用,这意味着如果多个JaCoCo代理应该在同一台机器上运行,则必须指定不同的端口。 6300
classdumpdir agent所调用到的所有class文件的目录。这可以用于调试目的,或者在动态创建类的情况下,例如当使用脚本引擎时。 没有转储
jmx 如果设置为true代理通过名称下的JMX 公开 功能org.jacoco:type=Runtime。请参阅下面的安全考虑。 false

2.4. 命令行界面

  JaCoCo带有一个命令行接口,可以通过命令行执行基本操作 (jacococli.jar)。对于复杂的用途,尤其是大型项目,请使用 JaCoCo 与各种构建工具的集成。
具体命令如下。每个命令都有可选和必选参数列表。某些参数可以多次指定以提供多个值。

  • dump ------Request execution data from a JaCoCo agent running in 'tcpserver' output mode.
  • instrument------Off-line instrumentation of Java class files and JAR files.
  • merge ------Merges multiple exec files into a new one.
  • report ------Generate reports in different formats by reading exec and Java class files.
  • classinfo ------Print information about Java class files at the provided location.
  • execinfo ------Print exec file content in human readable format.
  • version

三. 动态监测 Web 项目全量覆盖率

来看下具体配置

3.1. 服务端设置 JacocoAgent 并启动

  1. springBoot 项目为例
java -javaagent:/data/webapp/jacoco/jacocoagent.jar=includes=*,output=tcpserver,append=false,address=*,port=6200 -jar xxxxxxxxxx.jar 

请注意 java -jar 命令的使用方式:在jar包前面传进去的参数是给 jvm 启动用的,在jar包之后跟的参数是给main方法的。

  1. tomcat 项目为例
      我们常用的命令存在于:$CATALINA_HOME\bin下,有startup.sh和shutdown.sh,,其实这两个只是封装之后的脚本,底层调用的都是$CATALINA_HOME\bin\catalina.sh
set JAVA_OPTS=-server -Xms1024m -Xmx1024m -XX:PermSize=512M -XX:MaxNewSize=512m -XX:MaxPermSize=512m -Djava.awt.headless=true -javaagent:D:\AutoTest\jacoco\lib\jacocoagent.jar=includes=com.hundsun.*,output=tcpserver,port=8229,address=127.0.0.1 -Xverify:none
  1. weblogic 项目为例
    在~/bin/startWebLogic.sh下配置jacoco环境信息
JAVA_OPTIONS="${JAVA_OPTIONS} -javaagent:/home/weblogic/Oracle/Middleware/Oracle_Home/user_projects/domains/base_domain/bin/jacoco-0.8.3/lib/jacocoagent.jar=includes=*,output=tcpserver,address=192.168.10.26,port=6200 -Xverify:none"

3.2. 执行测试(接口测试、功能测试等)

这时可以对服务端进行测试,所有调服务端接口的操作都会记录作为代码覆盖率依据。

3.3. 抓取信息并生成报告

这步可以用jacococli.jar实现,也可以借助ant 或者maven。

3.3.1. 以 jacococli.jar为例

  1. 获取exec文件
java -jar jacococli.jar dump --address 192.169.110.1 --port 2014 --destfile ./jacoco-demo.exec

使用这个命令会在我们调用方的服务上生成一个exec文件

  1. 生成report
java -jar jacococli.jar report ./jacoco-demo.exec --classfiles /Users/oukotoshuu/IdeaProjects/demo/target/classes/com  --sourcefiles /Users/oukotoshuu/IdeaProjects/demo/src/main/java --html report --xml report.xml

注意这个classfiles和sourcefiles 可以是多个,我们如果是多模块项目通过指定代码路径和编译文件路径去分开做统计

3.3.2. 以 ant 为例

  1. 在项目根目录下新建一个build.xml文件,文件内容如下:
<?xml version="1.0" ?>
<project name="wftestReport" xmlns:jacoco="antlib:org.jacoco.ant" default="jacoco">

    <property name="server_ip" value="10.16.55.95"/>
    <property name="server_port" value="6200"/>
    <!--Jacoco的安装路径-->
    <property name="jacocoantPath" value="/data/jenkins/jacoco-0.8.4/lib/jacocoant.jar"/>
    <!--最终生成.exec文件的路径,Jacoco就是根据这个文件生成最终的报告的-->
    <property name="jacocoexecPath" value="./jacoco/mergetest.exec"/>
    <!--合并报告路径-->
    <property name="mergePath" value="./jacoco/all" />
    <!--生成覆盖率报告的路径-->
    <property name="reportfolderPath" value="./jacoco/report"/>
    <!--跑的是class,标注的是源码?-->

    <!--源代码路径-->
    <!--可以配置多个源代码-->
    <property name="express_src" value="/data/jenkins/.jenkins/workspace/TEST_ISTP_95/svn/src/main/java/com/isoftstone"/>

    <!--.class文件路径-->
    <property name="express_class" value="/data/jenkins/.jenkins/workspace/TEST_ISTP_95/svn/target/classes/com/isoftstone"/>

    <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml">
        <classpath path="${jacocoantPath}" />
    </taskdef>

    <!--  1 dump任务:获取覆盖率exec文件 -->
    <target name="dump">
        <jacoco:dump address="${server_ip}" reset="false" destfile="${jacocoexecPath}" port="${server_port}" append="true"/>
    </target>

    <!-- 2 合并exec文件 -->
    <!-- 获取指定目录下的所有 exec 文件并将数据合并为一个exec -->
    <target name="merge">
        <jacoco:merge destfile="./jacoco/merged.exec">
            <fileset dir="${mergePath}" includes="*.exec" />
        </jacoco:merge>
    </target>

    <!-- 3 生成覆盖率报告 
      根据前面配置的源代码路径和.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="Core">          
                    <classfiles>
                        <fileset dir="${express_class}" />
                    </classfiles>
                    <sourcefiles encoding="utf-8">
                        <fileset dir="${express_src}" />
                    </sourcefiles>
                </group>              
            </structure>
          <html destdir="${reportfolderPath}" encoding="utf-8" />    
          <csv destfile="D:\AutoTest\JRES\codeCoverage\report.csv" />         
        </jacoco:report>
  </target>
</project>
  1. 接着在build.xml文件目录下运行ant dump命令,在当前目录下出现一个.exec后缀名结尾的文件,出现如下图所示的代表运行成功

  2. 在build.xml文件目录下运行ant report命令,生成report

posted @ 2021-12-21 16:47  雨 燕  阅读(5415)  评论(1编辑  收藏  举报