zoukankan      html  css  js  c++  java
  • 持续集成第二①步:jenkins+jacoco+ant+apache集成统计web端功能测试覆盖率

    Jacoco原理:

    1.覆盖率定义

    作为一个测试人员,保证产品的软件质量是其工作首要目标,为了这个目标,测试人员常常会通过很多手段或工具来加以保证,覆盖率就是其中一环比较重要的环节。 我们通常会将测试覆盖率分为两个部分,即“需求覆盖率”和“代码覆盖率”。

    需求覆盖:指的是测试人员对需求的了解程度,根据需求的可测试性来拆分成各个子需求点,来编写相应的测试用例,最终建立一个需求和用例的映射关系,以用例的测试结果来验证需求的实现,可以理解为黑盒覆盖。

    代码覆盖:为了更加全面的覆盖,我们可能还需要理解被测程序的逻辑,需要考虑到每个函数的输入与输出,逻辑分支代码的执行情况,这个时候我们的测试执行情况就以代码覆盖率来衡量,可以理解为白盒覆盖。 以上两者完全可以相辅相成,用代码覆盖结果反向的检查需求覆盖(用例)的测试是否充分完整。

    2.jacoco工具介绍

    JaCoCo是一个开源的覆盖率工具(官网地址: http://www.eclemma.org/JaCoCo/ ),它针对的开发语言是java,其使用方法很灵活,可以嵌入到Ant、Maven中;可以作为Eclipse插件,可以使用其JavaAgent技术监控Java程序等等。 很多第三方的工具提供了对JaCoCo的集成,如sonar、Jenkins等。 JaCoCo包含了多种尺度的覆盖率计数器,包含指令级覆盖(Instructions,C0coverage),分支(Branches,C1coverage)、圈复杂度(CyclomaticComplexity)、行覆盖(Lines)、方法覆盖(non-abstract methods)、类覆盖(classes)。

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

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

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

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

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

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

    注入方式:On-the-fly插桩和Offline模式;

    On-the-fly:JVM中通过-javaagent参数指定特定的jar文件启动Instrumentation的代理程序,代理程序在通过Class Loader装载一个class前判断是否转换修改class文件,将统计代码插入class,测试覆盖率分析可以在JVM执行测试代码的过程中完成。

    Offline模式:在测试前先对文件进行插桩,然后生成插过桩的class或jar包,测试插过桩的class和jar包后,会生成动态覆盖信息到文件,最后统一对覆盖信息进行处理,并生成报告。

    On-the-fly和offline比较: On-the-fly模式更方便简单进行代码覆盖分析,无需提前进行字节码插桩,无需考虑classpath 的设置。

    存在如下情况不适合on-the-fly,需要采用offline提前对字节码插桩:

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

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

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

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

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

    Jacoco使用:

    1. 准备工作

    1.1参照:持续集成第一步

    1.2下载jacoco.zip包;

    1.3 将jacoco.zip包上传到服务器任一目录下(如:/opt/jacoco下)

    1.4 解压jacoco.zip: unzip jacoco.zip

    1.5 停掉tomcat8082服务;

    ./catalina.sh stop

    1.6 找到tomcat8082/bin/catalina.sh文件并打开,添加jacoco插件,指令如下:

    JAVA_OPTS="-javaagent:/opt/jacoco/lib/jacocoagent.jar=includes=*,output=tcpserver,port=8044,address=127.0.0.1 -Xverify:none"

    参数说明:

    1.6.1 /opt/jacoco/lib/jacocoagent.jar是放jacocoagent.jar文件的目录路径,按实际情况修改;

    1.6.2 includes是指要收集哪些类(注意不要光写包名,最后要写.*),不写的话默认是*,会收集应用服务上所有的类,包括服务器和其他中间件的类,一般要过滤(当然如果你愿意写*也完全没有问题,如'includes=*');

    1.6.3 output有4个值,分别是file,tcpserver,tcpclient,mbean,默认是file。使用file的方式只有在停掉应用服务的时候才能产生覆盖率文件,而使用tcpserver的方式可以在不停止应用服务的情况下下载覆盖率文件,后面会介绍如何使用dump方法来得到覆盖率文件。

    1.6.4 address是IP地址,IP就是Tomcat服务器的机器的IP;这里我们要在tomcat服务器上执行ant dump,直接写127.0.0.1;

    1.6.5 port 是端口(端口比较随便,找个能用的端口就行);

    1.6.6 '-Xverify:none':这个参数是防止启动主程序异常才加的(非强制,可以不加);

    1.7 启动tomcat8082

    ./catalina.sh start

    1.8 输入以下命令校验该步骤是否成功:

    ps -ef|grep jacocoagent.jar

    1.9 选择任意目录,新建build.xml文件(这里我们还是选择/opt目录下新建),文件内容如下:

    <?xml version="1.0" ?>
    <project name="jacoco" xmlns:jacoco="antlib:org.jacoco.ant" default="jacoco">
        <!--Jacoco的安装路径-->
      <property name="jacocoantPath" value="/opt/jacoco/lib/jacocoant.jar"/>
      <!--最终生成.exec文件的路径,Jacoco就是根据这个文件生成最终的报告的-->
      <property name="jacocoexecPath" value="/opt/jacoco.exec"/>
        <!--生成覆盖率报告的路径-->
      <property name="reportfolderPath" value="/opt/jacoco_report"/>
      <!--远程Tomcat服务的ip地址-->
      <property name="server_ip" value="127.0.0.1"/>
      <!--前面配置的远程Tomcat服务打开的端口,要跟上面配置的一样-->
      <property name="server_port" value="8044"/>
      <!--本地源代码路径,ps:此处Autotest为jenkins任务名称-->
      <property name="checkOrderSrcpath" value="/root/.jenkins/workspace/Autotest/src/main/java"/>
      <!--本地.class文件路径,ps:此处Autotest为jenkins任务名称-->
      <property name="checkOrderClasspath" value="/root/.jenkins/workspace/Autotest/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="false"/>
      </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 Order 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>
    

    1.10 校验:进入到build.xml所在的目录,输入ant dump,出现如下图所示表示成功;

    2. jenkins配置与报告查看

    2.1 jenkins安装jacoco插件

    打开jenkins-系统管理-管理插件-可选插件-安装JaCoCo plugin;

    2.2 jenkins jobs设置

          2.2.1 新建job       2.2.2 git源码设置

    2.2.3 构建步骤: 选择执行shell,shell内容:

    #!/bin/bash
    chmod -R 777 ${WORKSPACE}/pom.xml
    python /opt/editpom.py
    mvn install -Ptest1
    echo "编译成功,已生成编译文件"
    

    命令释义:

    2.2.3.1 python /opt/editpom.py #该操作的目的是为了在pom.xml中插入jacoco的配置

    with open('pom.xml','r+') as f1:
        data = f1.read()
    data1 = data.replace(" ","")
    data2 = data1.replace('<projectxmlns','<project xmlns').replace('4.0.0"xmlns','4.0.0" xmlns').replace('http://maven.apache.org/POM/4.0.0http:','http://maven.apache.org/POM/4.0.0 http:')
    data3=data2.replace("</plugins>
    </pluginManagement>","<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.7.9</version>
    <executions>
    <execution>
    <id>default-prepare-agent</id>
    <goals>
    <goal>prepare-agent</goal>
    </goals>
    <configuration>
    <destFile>
    ${project.build.directory}/coverage-reports/jacoco.exec
    </destFile>
    <propertyName>surefireArgLine</propertyName>
    </configuration>
    </execution>
    <execution>
    <id>default-report</id>
    <phase>test</phase>
    <goals>
    <goal>report</goal>
    </goals>
    <configuration>
    <dataFile>${project.build.directory}/coverage-reports/jacoco.exec</dataFile>
      <outputDirectory>${project.reporting.outputDirectory}/jacoco</outputDirectory>
     </configuration>
    </execution>
    </executions>
    </plugin>
    </plugins>
    </pluginManagement>")
    with open('pom.xml','w+') as f2:
        f2.write(data3)
    

    备注:

    此处的前提是pom.xml文件最终打的是war包;若不是,则需要修改pom.xml文件,此处py文件需做相应修改;

    with open('pom.xml','r+') as f1:
        data = f1.read()
    data1 = data.replace(" ","")
    data2 = data1.replace('<projectxmlns','<project xmlns').replace('4.0.0"xmlns','4.0.0" xmlns').replace('http://maven.apache.org/POM/4.0.0http:','http://maven.apache.org/POM/4.0.0 http:')
    data3 = data2.replace('<modelVersion>4.0.0</modelVersion>','<modelVersion>4.0.0</modelVersion>
    <packaging>war</packaging>')
    data4=data3.replace("</plugins>
    </pluginManagement>","<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <configuration>
    <failOnMissingWebXml>false</failOnMissingWebXml>
    </configuration>
    </plugin>
    <plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.7.9</version>
    <executions>
    <execution>
    <id>default-prepare-agent</id>
    <goals>
    <goal>prepare-agent</goal>
    </goals>
    <configuration>
    <destFile>
    ${project.build.directory}/coverage-reports/jacoco.exec
    </destFile>
    <propertyName>surefireArgLine</propertyName>
    </configuration>
    </execution>
    <execution>
    <id>default-report</id>
    <phase>test</phase>
    <goals>
    <goal>report</goal>
    </goals>
    <configuration>
    <dataFile>${project.build.directory}/coverage-reports/jacoco.exec</dataFile>
      <outputDirectory>${project.reporting.outputDirectory}/jacoco</outputDirectory>
     </configuration>
    </execution>
    </executions>
    </plugin>
    </plugins>
    </pluginManagement>")
    with open('pom.xml','w+') as f2:
        f2.write(data4)
    

    2.2.3.2 mvn install -Ptest1 选择test1环境并进行编译(所有的配置文件都在profile里面,编译需要将profile编译到对应的properties里面;具体命令可跟开发核对)

    此处有坑:

    执行mvn install时会报错,是因为我们需要修改下maven的settings.xml文件(文件位置为/usr/local/maven/conf/settings.xml),该文件可找开发索要,直接覆盖原有文件即可;----因涉及公司信息,此处就不放上文件内容了

    2.2.4 安装PostBuildScript插件(插件管理-可选插件,搜索PostBuildScript,点击安装 )

    2.2.5 构建后操作执行部署

    添加shell内容:

    #!/bin/bash
    TOMCAT_HOME="/usr/local/tomcat/tomcat8082/"
    tomcat_pid=`/usr/sbin/lsof -n -P -t -i :8082`
    echo "关闭tomcat8082"
    [ -n "$tomcat_pid" ] && kill -9 $tomcat_pid
    echo "删除原有war包"
    cd "$TOMCAT_HOME"/webapps
    rm -rf jianlc-mgmt
    rm -rf jianlc-mgmt.war
    echo "部署新的war包"
    cp ${WORKSPACE}/target/jianlc-mgmt.war "$TOMCAT_HOME"/webapps/
    cd "$TOMCAT_HOME"/bin
    ./catalina.sh start
    echo "部署成功,请开始测试!"
    

    点击保存并开始构建。

    PS:重启时记得查看canalina.out日志,若发现日志报java.net.UnknownHostException异常,则需要做如下操作:

    1.查看服务器的hostname:  # hostname
    2.进入etc/hosts编辑,在下面加一行:
    127.0.0.1 查询到的hostname
    3. 保存退出,重启tomcat,解决!
    

    2.2.6 访问项目: http://IP:8082/项目名称/,进行功能测试;

    2.2.7 测试完成后在build.xml目录下依次执行以下命令:

    ant dump
    ant report
    

    2.2.8 在线查看report报告:

    2.2.8.1 安装httpd并打开apache配置文件

    #安装httpd
    yum -y install httpd
    #安装完成后打开配置文件
    vi /etc/httpd/conf/httpd.conf
    

    #输入/DirectoryIndex查找,行尾新增index.html

    #再输入/Listen查找,端口号改为8033

    保存并退出;

    2.2.8.2 将/opt/jacoco_report目录下的所有文件复制到/var/www/html/目录下;

    cp -r -f /opt/jacoco_report/* /var/www/html/
    

    2.2.8.3 重启apache

    service httpd restart
    

    坑:

    若重启发现报如下错误,则需修改/etc/selinux/config文件找到SELINUX=enforcing 修改为SELINUX=disable(见图2),之后执行

    setenforce 0
    

    2.2.8.4 访问http://ip:8033/index.html

    PS:报告解读

    • Instructions: Java 字节指令的覆盖率。执行的最小单位,和代码的格式无关。
    • Branches: 分支覆盖率。注意,异常处理不算做分支。
    • Cxty(Cyclomatic Complexity): 圈复杂度, Jacoco 会为每一个非抽象方法计算圈复杂度,并为类,包以及组(groups)计算复杂度。圈复杂度简单的说就是为了覆盖所有路径,所需要执行单元测试数量,圈复杂度大说明程序代码可能质量低且难于测试和维护。
    • Lines: 行覆盖率,只要本行有一条指令被执行,则本行则被标记为被执行。
    • Methods: 方法覆盖率,任何非抽象的方法,只要有一条指令被执行,则该方法被计为被执行。
    • Classes: 类覆盖率,所有类,包括接口,只要其中有一个方法被执行,则标记为被执行。注意:构造函数和静态初始化块也算作方法。

    钻石代表分支覆盖情况

    • 红色钻石:这一行没有分支被执行
    • 黄色钻石:这一行中只有部分分支被执行
    • 绿色钻石:这一行的所有分支都被执行

    背景颜色代表指令覆盖率

    • 红色背景:这一行并没有任何指令被执行
    • 黄色背景:这一行的部分指令被执行
    • 绿色背景:这一行的所有指令都被执行了
  • 相关阅读:
    angular入门学习文档之一
    将SDL程序变成网页(使用emscripten)
    Nim使用OpenGL
    C++移动操作,RVO和NRVO
    从零开始制作一个粒子系统
    cocos2d-x学习之路(一)——安装cocos2d-x
    堆排序
    开发zeroc ice应用入门(java开发ice应用,python开发ice应用,java与python结合开发ice服务)
    eclipse自动提示设置以及问题:去除变量自动提示(图文详解)
    ubuntu安装新版QQ
  • 原文地址:https://www.cnblogs.com/cocc/p/12365765.html
Copyright © 2011-2022 走看看