JaCoCo在Tomcat服务器上监控代码覆盖率的使用方法

简介

Jacoco是一个开源的覆盖率工具。Jacoco可以嵌入到Ant 、Maven中,并提供了EclEmma Eclipse插件,也可以使用JavaAgent技术监控Java程序。很多第三方的工具提供了对Jacoco的集成,如sonar、Jenkins等。

官网地址:http://www.eclemma.org/jacoco/

Jacoco包含了多种尺度的覆盖率计数器,包含指令级(Instructions,C0coverage),分支(Branches,C1coverage)、圈复杂度(CyclomaticComplexity)、行(Lines)、方法(non-abstract methods)、类(classes)。

²  行覆盖率:度量被测程序的每行代码是否被执行,判断标准行中是否至少有一个指令被执行。

²  类覆盖率:度量计算class类文件是否被执行。

²  分支覆盖率:度量if和switch语句的分支覆盖情况,计算一个方法里面的总分支数,确定执行和不执行的分支数量。

²  方法覆盖率:度量被测程序的方法执行情况,是否执行取决于方法中是否有至少一个指令被执行。

²  指令覆盖:计数单元是单个java二进制代码指令,指令覆盖率提供了代码是否被执行的信息,度量完全 独立源码格式。

²  圈复杂度:在(线性)组合中,计算在一个方法里面所有可能路径的最小数目,缺失的复杂度同样表示测 试案例没有完全覆盖到这个模块。

目前支持的启动类型:

²  Local Java application java程序(有main)

²  Eclipse/RCP application 桌面应用程序

²  Equinox OSGi framework

²  JUnit test 单元测试

²  TestNG test

²  JUnit plug-in test

²  JUnit RAP test

²  SWTBot test

²  Scala application

 

覆盖率工具工作流程

 

1. 对Java字节码进行插桩,On-The-Fly和Offine两种方式。 
2. 执行测试用例,收集程序执行轨迹信息,将其dump到内存。 
3. 数据处理器结合程序执行轨迹信息和代码结构信息分析生成代码覆盖率报告。 
4. 将代码覆盖率报告图形化展示出来,如html、xml等文件格式。

 

插桩原理

 

主流代码覆盖率工具都采用字节码插桩模式,通过钩子的方式来记录代码执行轨迹信息。其中字节码插桩又分为两种模式On-The-Fly和Offine。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冲突
          无法自定义用户加载类

 

JaCoCo作用

监测应用程序在测试时有多少源码被执行。

测试包括:单元测试、功能测试、接口测试
执行包括:指令、分支、圈复杂度、行、方法、类

 

代码覆盖率的意义

Martin Fowler(重构那本书的作者)曾经写过一篇博客来讨论这个问题,他指出:把测试覆盖作为质量目标没有任何意义,而我们应该把它作为一种发现未被测试覆盖的代码的手段。

 

1、分析未覆盖部分的代码,从而反推在前期测试设计是否充分,没有覆盖到的代码是否是测试设计的盲点,为什么没有考虑到?需求/设计不够清晰,测试设计的理解有误,工程方法应用后的造成的策略性放弃等等,之后进行补充测试用例设计。

2、检测出程序中的废代码,可以逆向反推在代码设计中思维混乱点,提醒设计/开发人员理清代码逻辑关系,提升代码质量。
3、代码覆盖率高不能说明代码质量高,但是反过来看,代码覆盖率低,代码质量不会高到哪里去,可以作为测试自我审视的重要工具之一。

 

影响

1、进行完全的覆盖性测试,提升设计能力; ---》对于测试
2、检验是否存在冗余代码,理清逻辑思路; ---》对于开发

准备工作

下载jacoco,进入jacoco官网:http://www.eclemma.org/jacoco/ 

保证当前JDK在1.5以上

获取到jacoco包jacoco-0.8.2.zip:

Windows下直接解压,得到   D:\JAR\jacoco-0.8.2   目录自定义即可

目录结构图如下:

Jacoco的lib目录结构如下:

 

其中jacocoagent.jar是放置在Tomcat配置中作为代理同时启动的jar包

 

对于jacocoant.jar是利用ant对覆盖率进行处理的jar包

 

着重介绍和使用jacococli.jar,此包可直接在客户端上执行,无需其他软件。

 

 

Tomcat配置

 

 

1 关闭Tomcat, window是shutdown.bat扩展名、Linux是shutdown.sh扩展名

 

2 修改Windows下catalina.bat,在第二行添加如下脚本:

 

set JAVA_OPTS=-javaagent:D:\JAR\jacoco-0.8.2\lib\jacocoagent.jar=includes=*,output=tcpserver,port=9527,address=127.0.0.1,append=true -Xverify:none

 

   Linux修改catalina.sh,在第二行添加如下脚本:

 

JAVA_OPTS="-javaagent:/home/systemtools/jacoco-0.8.2/lib/jacocoagent.jar=includes=*,output=tcpserver,port=9527,address=192.168.43.100,append=true -Xverify:none"

  

 

JAVA_OPTS="-javaagent:[yourPath/]jacocoagent.jar=includes=com.companyName.*,output=tcpserver,port=PORT,address=IP -Xverify:none"
参数说明:
1. yourPath 是放 jacocoagent.jar 文件的目录路径;那么 `jacocoagent.jar` 这个 `jar` 包的路径就是在准备工作里下载下来的 `zip` 包,解压之后的 `lib` 目录下。
2. includes 是指要收集哪些类(注意不要光写包名,最后要写.*),不写的话默认是*,会收集应用服务上所有的类,包括服务器和其他中间件的类,一般要过滤(当然如果你愿意写*也完全没有问题,如:`includes=com.*` or `includes=*`);
3. output 有 4 个值,分别是 file、tcpserver、tcpclient、mbean,默认是 file。使用 file 的方式只有在停掉应用服务的时候才能产生覆盖率文件,而使用 tcpserver 的方式可以在不停止应用服务的情况下下载覆盖率文件,后面会介绍如何使用 dump 方法来得到覆盖率文件。
4. address 是 IP 地址,IP 就是 Tomcat 服务器的机器的 IP,至于是写 `服务器本机的 IP` 还是写 `127.0.0.1` 要看情况 1) 如果是在 Tomcat 服务器上执行 `ant dump` 的话,就直接写 `address=127.0.0.1` 2) 如果执行 `ant dump` 不是在 Tomcat 服务器上执行的,就得写服务器本机的IP(切记)
5. port 是端口(端口比较随便,找个能用的端口就行,直接我为什么将端口写成 `8044`,我的想法是 `BUG 死死` 与 `8044` 挺配的,所以就用它作为端口号了) (`address` 和 `port` 是使用 tcpserver 方式需要的 2 个参数,也是执行 ant dump 方法必须要用到的。)
6. `-Xverify:none`:这个参数是防止启动主程序异常才加的(非强制,可以不加)

 

3 启动Tomcat,Windows下使用startup.bat,Linux下使用startup.sh

 

4 验证JAVA_OPTS是否有修改正确

 

     Windows下在cmd窗口使用WMIC命令,回车

 

        回显:wmic:root\cli>

 

        在>后面输入:process where name="java.exe"  命令

 

        查看回显信息

 

 

注意:可能信息很长,而且在一行,需要拖动下方的滚动条查看

 

 

      Linux下载终端窗口中输入ps -ef | grep tomcat

jacocoagent.jar的使用参数:

 

 

使用jacococli.jar包

 

 

使用释疑

 

 

查看帮助,命令为:java -jar jacococli.jar --help

 

 

对于具体的command查看帮助信息。

 

命令如下:

 

java -jar jacococli.jar dump –help       # 获取服务器端tcpserver模式输出文件jacoco.exec

 

具体参数信息:

 

 

java -jar jacococli.jar instrument --help   # 离线java class文件和jar文件的指令集

 

 

java -jar jacococli.jar merge --help    #合并多个生成的.exec文件为一个新文件

java -jar jacococli.jar report --help     #通过读取.exec和java的class文件生成不同文件格式的报告

java -jar jacococli.jar classinfo --help     # 在指定位置打印有关Java类文件的信息

 

java -jar jacococli.jar execinfo --help    # 以高可读性打印exec文件信息

 

 

实例展示

 

 

以上面配置的Linux下Tomcat为例,使用命令:

java -jar jacococli.jar dump --address 192.168.43.100 --port 9527 --destfile ./jacoco.exec

 

引入Jenkins

 

 

配置

 

 

由于Jenkins提供了jacoco的插件,我们可以通过Jenkins来解析jacoco的exec文件

 

安装jacoco插件,路径:Jenkins > 系统管理 > 插件管理,在过滤框中输入jacoco进行搜索

 

 

 

选中后,点击【直接安装】按钮

 

安装成功后可以在插件管理的已安装标签页查看:

 

 

回到Jenkins首页,点击【新建任务】创建一个自由风格的job,名称为:TestJacoco

 

在<配置>中,使用git获取GitHub上的代码================》图略了。。。。。

 

 

在构建中,【增加构建步骤】,选择<执行Windows批处理命令>(若Jenkins机器搭建在Linux下,可以选择<执行shell>)

 

 

点击左下角的【保存】按钮。

 

执行<立即构建>

 

此时可以手动将jacococli.jar包放置到TestJacoco的工作目录下。

 

目录例如:

 

C:\Users\username\.jenkins\workspace\TestJacoco

将编译好的.class 文件的文件夹,放置在jacococli.jar包的同一个目录下。

此时,在<配置>中【增加构建后操作】选择:<Record JaCoCo coverage report>

 

 

点击【保存】

 

然后执行<立即构建>

 

可以在工程的目录下查看到如图所示信息:

 

 

点击图中区域任意位置,可以跳转到如下:

 

 

可以通过图中信息,查看所有代码的覆盖率信息。

 

针对其他的命令的使用,在这里不做逐一的解释。有兴趣可以按照参数列表进行探究。

 

 针对Springboot - 多module - 代码覆盖率统计

说明:web为启动模块,依赖关系:web->service->manager->dao->entity+common

新建了test模块,当前模块里什么代码都没有,但是POM中显式依赖了其他所有模块,显式依赖就是为了聚合其他所有模块的统计报告;

<?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>com.mx.server.tsp</groupId>
    <artifactId>pom-management-test</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>pom-management-test</name>

    <parent>
        <groupId>com.mx.server.tsp</groupId>
        <artifactId>pom-management</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <dependencies>
        <!-- 依赖所有子模块  -->
        <dependency>
            <groupId>com.mx.server.tsp</groupId>
            <artifactId>pom-management-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mx.server.tsp</groupId>
            <artifactId>pom-management-service</artifactId>
        </dependency>

        <dependency>
            <groupId>com.mx.server.tsp</groupId>
            <artifactId>pom-management-manager</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mx.server.tsp</groupId>
            <artifactId>pom-management-dao</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mx.server.tsp</groupId>
            <artifactId>pom-management-entity</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mx.server.tsp</groupId>
            <artifactId>pom-management-common</artifactId>
        </dependency>
    </dependencies>
    
</project>

  

 

 注:test模块是为了统一代码覆盖率报告而单独建的模块,且test->web+service+manager+dao+entity+common(test依赖其他所有子模块)

 

综上,为了合并多module的代码测试覆盖率统计报告,

(1)采用了JaCoCo插件,

(2)且单独为了聚合统计报告而新建了一个test模块,当前模块没有任何代码,只是显式依赖了其他所有子模块,

(3)在项目根目录下执行mvn install后,即可通过test模块下的target/site/jacoco-aggregate/index.html查考到合并后的代码测试覆盖率报告;

 

针对具体的jacoco插件,进行数据分析。执行mvn test获取结果。

 

            

    <!-- 单元覆盖率插件-->
           <plugin>
              <groupId>org.jacoco</groupId>
              <artifactId>jacoco-maven-plugin</artifactId>
              <version>0.7.9</version>
              <executions>
                  <execution>
                  <id>pre-unit-test</id>
                  <goals>
                      <goal>prepare-agent</goal>
                  </goals>
                  <configuration>
                      <propertyName>jacocoArgLine</propertyName>
                  </configuration>
                  </execution>
                  <execution>
                  <id>post-unit-test</id>
                  <phase>package</phase>
                  <goals>
                      <goal>report</goal>
                  </goals>
                  </execution>
              </executions>
            </plugin>

<!---按照需要选择插件数据---->

<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.7.201606060606</version>
<executions>
<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>
<execution>
<id>post-unit-test</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<dataFile>target/jacoco.exec</dataFile>
<outputDirectory>target/jacoco-out</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<!------->

        <plugin>                 <groupId>org.apache.maven.plugins</groupId>                 <artifactId>maven-surefire-plugin</artifactId>                 <version>2.9</version>                 <configuration>                     <argLine>-Xmx256M ${jacocoArgLine}</argLine>                     <skip>false</skip>                     <testFailureIgnore>false</testFailureIgnore>                     <includes></includes>                     <excludes>                         <!-- 由于测试controller类需要启动auth应用进行登录请求,故剔除 -->                         <exclude>**/controller/*ControllerTest.java</exclude>                     </excludes>                 </configuration>             </plugin>

  

 

 

=======================END=========================

posted @ 2018-09-18 11:17  wozijisun  阅读(6690)  评论(0编辑  收藏  举报