Jacoco获取集成测试覆盖率 朱雀 2022-11-29 05:56 600阅读 0赞 #### 一、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/][https_www.eclemma.org_jacoco] 2. 将jacoco.jar解压,cd到jacoco项目的lib目录下。jacocoagent.jar 就是启动应用时主要用来插桩的jar包 ![在这里插入图片描述][format_png_pic_center] ⚠️:请注意不要写错名称,里面有个很像的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参数 ![在这里插入图片描述][20200819184236983.png_pic_center] 将项目war包放到tomcat的webapps目录下(tomcat会自动将war包解压) 启动tomcat `sudo sh ./startup.sh`,使用命令 `ps aux|grep tomcat`查看tomcat进程。若进程信息中包含javaagent的内容表示插桩成功 ![在这里插入图片描述][20200819185614504.png_pic_center] **方式三: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] [Jacoco Code Coverage][] [JaCoCo在Tomcat服务器上监控代码覆盖率的使用方法][JaCoCo_Tomcat] [https://www.cnblogs.com/dingtian/p/7754079.html][https_www.cnblogs.com_dingtian_p_7754079.html] [https_www.eclemma.org_jacoco]: https://www.eclemma.org/jacoco/ [format_png_pic_center]: /images/20221124/dba550b2cb43455f83528c62331e06b5.png [20200819184236983.png_pic_center]: /images/20221124/a9eddcd2df824336b78ff2b1082ce8ab.png [20200819185614504.png_pic_center]: /images/20221124/92034aa276004b3f999a2c15aa620cd8.png [Jacoco]: https://testerhome.com/topics/20632 [Jacoco Code Coverage]: https://www.jianshu.com/p/16a8ce689d60 [JaCoCo_Tomcat]: https://blog.csdn.net/weixin_30949361/article/details/96021454?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~first_rank_v2~rank_v25-16-96021454.nonecase&utm_term=tomcat%E9%85%8D%E7%BD%AEjacoco [https_www.cnblogs.com_dingtian_p_7754079.html]: https://www.cnblogs.com/dingtian/p/7754079.html
还没有评论,来说两句吧...