目录
三、公司项目级docker容器化项目Super-jacoco应用
3.3.2 配置Super-jacoco服务application.properties信息
一、介绍
Super-jacoco 在jacoco 上发展而来,可以统计增量代码覆盖率的统计。
jacoco 相关说明:jacoco代码覆盖率_jacoco覆盖率_做测试的喵酱的博客-CSDN博客
本文介绍 Super-jacoco 统计手工用例代码覆盖率的统计数据。
主要分两部分:
一、自己在本地搭建Super-jacoco,自己玩一玩
二、在公司的容器化项目中,如何使用Super-jacoco统计手工用例的代码覆盖率
GitHub项目地址:
尽量使用git命令,直接下载:
git clone https://github.com/didi/super-jacoco.git
二、自己在本地搭建Super-jacoco服务
2.1 准备工作
- 准备gitlab用户
- 准备一个MySQL数据库5.x或者8.x版本
2.2 部署super jacoco服务
1、下载super jacoco 项目
2、初始化数据库
在super jacoco 项目中,sql/coverage_report.sql 文件用于初始化数据库。
CREATE DATABASE `super-jacoco` DEFAULT CHARACTER SET utf8 COLLATE utf8_bin ;
CREATE TABLE `diff_coverage_report` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`job_record_uuid` varchar(80) NOT NULL COMMENT '请求唯一标识码',
`request_status` int(10) NOT NULL COMMENT '请求执行状态,1=下载代码成功,2=生成diffmethod成功,3=生成报告成功,-1=执行出错',
`giturl` varchar(80) NOT NULL COMMENT 'git 地址',
`now_version` varchar(80) NOT NULL COMMENT '本次提交的commidId',
`base_version` varchar(80) NOT NULL COMMENT '比较的基准commitId',
`diffmethod` mediumtext COMMENT '增量代码的diff方法集合',
`type` int(11) NOT NULL DEFAULT '0' COMMENT '2=增量代码覆盖率,1=全量覆盖率',
`report_url` varchar(300) NOT NULL DEFAULT '' COMMENT '覆盖率报告url',
`line_coverage` double(5,2) NOT NULL DEFAULT '-1.00' COMMENT '行覆盖率',
`branch_coverage` double(5,2) NOT NULL DEFAULT '-1.00' COMMENT '分支覆盖率',
`err_msg` varchar(1000) NOT NULL DEFAULT '' COMMENT '错误信息',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`sub_module` varchar(255) NOT NULL DEFAULT '' COMMENT '子项目目录名称',
`from` int(10) NOT NULL DEFAULT '0' COMMENT '1=单元测试,2=环境部署1=单元测试,2=hu',
`now_local_path` varchar(500) NOT NULL DEFAULT '',
`base_local_path` varchar(500) NOT NULL DEFAULT '',
`log_file` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`job_record_uuid`),
KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 COMMENT='增量代码覆盖率';
CREATE TABLE `diff_deploy_info` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`job_record_uuid` varchar(80) NOT NULL COMMENT '请求唯一标识码',
`address` varchar(15) NOT NULL COMMENT 'HOST',
`port` int(10) NOT NULL COMMENT '端口',
`code_path` varchar(1000) NOT NULL DEFAULT '' COMMENT 'nowVersion代码目录',
`child_modules` varchar(1000) NOT NULL DEFAULT '' COMMENT '项目子模块名称',
PRIMARY KEY (`job_record_uuid`),
KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 COMMENT='服务部署地址';
3、配置application.properties
在下载下来的项目中,application.properties配置信息
src/main/resources/application.properties
# 以下信息需要手动配置
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/super-jacoco?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
#git username & password
gitlab.username=miaojiang
gitlab.password=mj123456
主要配置
数据库的连接信息:url 账号 密码
gitlab 的账号、密码
注意:
数据库的驱动配置,5.x与8.x 版本的MySQL,驱动是不一致的,根据自己的数据库配置。
如果你的数据库为MySQL 8.x,需要修改项目中的驱动。
1、修改pom.xml文件
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.didichuxing.chefuqa</groupId>
<artifactId>super-jacoco</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>super-jacoco</name>
<description>project for Spring cloud eureka client</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<entity.target.dir>src/main/java/</entity.target.dir>
<dao.resources.dir>src/main/resources/</dao.resources.dir>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
<mybatis-spring-boot>1.2.0</mybatis-spring-boot>
<mysql-connector>8.0.26</mysql-connector>
<JAVA_HOME>/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home</JAVA_HOME>
</properties>
<dependencies>
<dependency>
<groupId>org.jodd</groupId>
<artifactId>jodd-core</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.jacoco</groupId>
<artifactId>org.jacoco.core</artifactId>
<version>0.8.4</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>7.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.16</version>
</dependency>
<!-- MySQL 连接驱动依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector}</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>15.0</version>
</dependency>
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.sharegov</groupId>
<artifactId>mjson</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.3</version>
</dependency>
<!-- pinyin-->
<dependency>
<groupId>com.belerweb</groupId>
<artifactId>pinyin4j</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom2</artifactId>
<version>2.0.6</version>
</dependency>
<!-- ssh的依赖 -->
<dependency>
<groupId>ch.ethz.ganymed</groupId>
<artifactId>ganymed-ssh2</artifactId>
<version>build210</version>
</dependency>
<dependency>
<groupId>org.tmatesoft.svnkit</groupId>
<artifactId>svnkit</artifactId>
<version>1.8.7</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.5</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.2.3</version>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>4.9.0.201710071750-r</version>
</dependency>
<dependency>
<groupId>com.github.javaparser</groupId>
<artifactId>javaparser-core</artifactId>
<version>3.4.4</version>
</dependency>
<!-- Swagger2核心包 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<!-- jsoup HTML parser library @ http://jsoup.org/ -->
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.10.2</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.0.2</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>super-jacoco</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArguments>
<bootclasspath>${java.home}/lib/rt.jar:${java.home}/lib/jce.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>
<configurationFile>
${basedir}/src/main/resources/tk-mybatis-autogen.xml
</configurationFile>
<!--允许移动生成的文件 -->
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.1.5</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
2、application.properties修改数据库配置驱动
下面这个是8.x MySQL的驱动
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
4、编译super jacoco项目
修改完super jacoco项目中的配置文件,就可以编译该项目了。生成super jacoco.jar
5、部署 super jacoco 服务
启动super-jacoco.jar 服务
执行命令
nohup java -jar super-jacoco.jar &
默认端口为8899
可以8899端口,程序是否启动成功
lsof -i:8899
注意:
在部署 super jacoco 服务的机器上,一定要有以下环境:
1、有mvn环境,如果没有的话,需要配置
2、有git环境,并配置了能拉取被测项目的gitlab账号和密码。
可以自己在这台机器上手动试一下,是否有权限
git clone xxx.xxx.git
相关资料:
Mac下安装Maven&idea配置maven_做测试的喵酱的博客-CSDN博客
mac&linux配置环境变量详解_vim ~/.zshrc-CSDN博客
2.3 启动被测项目
1、在super jacoco项目中,找到jacoco/org.jacoco.agent-0.8.5-runtime.jar 文件。
将org.jacoco.agent-0.8.5-runtime.jar 文件,放在被测服务部署的项目上。
如放置路径为/usr/local/org.jacoco.agent-0.8.5-runtime.jar
被测项目为 mock-server.jar
启动被测项目:
nohup java -javaagent:/usr/local/org.jacoco.agent-0.8.5-runtime.jar=includes=*,output=tcpserver,port=18513,address=*,append=true -jar mock-server.jar > /dev/null 2>&1 &
命令解释:
-
nohup
:忽略挂起信号,允许命令在后台运行。 -
java
:启动 Java 虚拟机。 -
-javaagent:/usr/local/org.jacoco.agent-0.8.5-runtime.jar=includes=*,output=tcpserver,port=18513,address=*,append=true
:配置 JaCoCo 代码覆盖率工具代理,其中:-
/usr/local/org.jacoco.agent-0.8.5-runtime.jar
:指定 JaCoCo 代理 Jar 文件的路径。 -
includes=*
:指定要包括在覆盖率报告中的类的匹配模式。此处将所有类都包括在内。 -
output=tcpserver
:指定覆盖率信息的输出模式。此处使用 TCP 服务器模式。 -
port=18513
:指定 TCP 服务器的端口号。 -
address=*
:指定 TCP 服务器的地址。此处使用星号*
表示接受所有来自任何地址的连接。 -
append=true
:指定是否将新的覆盖率数据附加到现有数据文件中。此处附加新的数据以保留历史数据。
-
-
-jar mock-server.jar
:指定要运行的 Jar 文件,即 MockServer 的可执行 Jar 文件。 -
> /dev/null
:将标准输出重定向到/dev/null
,即不输出任何信息。 -
2>&1
:将标准错误输出重定向到标准输出。 -
&
:将进程置于后台运行。
注意:
port=18513 端口,是在被测试的服务所在服务器上,18513端口上,启一个jacoco监听的服务,一定要保证部署被测项目的机器,放开了18513 端口,且我们有权限访问18513 端口。
启动被测项目后,我们可以查看18513 端口的服务是否启动成功
lsof -i:18513
2.4、代码覆盖率收集
假如:
super jacoco 服务部署在了 123.123.123.123 这台服务上了
被测项目mock-server 部署在了 456.456.456.456 机器上了
2.4.1 启动代码覆盖率收集
1)启动覆盖率收集URL:
http://123.123.123.123 /cov/triggerEnvCov
调用方法:POST参数(body方式传入):
{
"uuid": "uuid",
"type": 1,
"gitUrl": "git@git",
"subModule": "",
"baseVersion": "master",
"nowVersion": "master",
"address": "456.456.456.456",
"port": "18513"
}
备注:
IP和port为被测服务所在服务器的IP和端口
uuid: 自定义的一个id,将来拉取覆盖率时,通过这个uuid来拉取
gitUrl: 被测项目的gitlab地址
返回:
{
"code": 200,
"data": true,
"msg": "msg"
}
2.4.2 获取代码覆盖率
覆盖率结果URL:
http://123.123.123.123/cov/getEnvCoverResult
调用方法:
GET
参数:
uuid(String)
返回:
{
"code": 200,
"data": {
"coverStatus": 1,
"errMsg": "msg",
"lineCoverage": 100.0,
"branchCoverage": 100.0,
"logFile": "file content",
"reportUrl": "http://"
},
"msg": "msg"
}
三、公司项目级docker容器化项目Super-jacoco应用
前提:
需要运维、网络、研发、测试同学的相互配合。
但是整体流程,其实和上面是一致的。
下载Super-jacoco 项目:
git clone https://github.com/didi/super-jacoco.git
然后取出Super-jacoco 项目 中的 jacoco/org.jacoco.agent-0.8.5-runtime.jar 文件,与sql/coverage_report.sql文件。
3.1 镜像准备
本部分公司,主要是由运维同学负责。
举例:
依然是部署mock-server服务。
之前的流程:
运维: 准备 Dockerfile文件---生成镜像
部署被测项目:拉取镜像,拉取被测项目的代码,进行编译,生成jar包。在容器中启动该项目
应用jacoco流程:
运维: 先把jacoco agent 的jar包打 放到某一个公共服务器的位置,
然后编写 Dockerfile文件,在里面写拉jacoco agent jar包的命令。 最后再通过这个Dockerfile文件生成的image镜像
部署被测项目:
拉取镜像,拉取被测项目的代码,进行编译,生成jar包。在容器中启动该项目。需要配置JAVA_OPTS的环境变量,把java agent信息配置到变量里
-javaagent:/usr/local/org.jacoco.agent-0.8.5-runtime.jar=includes=*,output=tcpserver,port=18513,address=*,append=true
服务的启动命令是
java $JAVA_OPTS -jar xxx.jar
这样,就拼接成了完整的运行命令:
java -javaagent:/usr/local/org.jacoco.agent-0.8.5-runtime.jar=includes=*,output=tcpserver,port=18513,address=*,append=true -jar mock-server.jar
运维同学要注意:
要放开18513 这个端口,如果要对该端口做代理的话:
jacoco并非http/https,如果测试环境的k8s集群里面,访问形式为ingress+svc的形式,ingress配置域名,svc配置ClusterIP的模式。但是这个ingress只能代理http/https两种协议的请求。这种代理模式是不通的。需要把svc的ClusterIP改为了NodePort的模式,就可以直接通过公共节点+PORT进行访问了。
3.2 部署项目
负责部署项目的同学 测试/研发,在自己公司的部署平台上,选择上面创建好的镜像,配置部署项目的命令。
3.3 测试同学部署Super-jacoco服务
3.3.1 、数据库准备,用于存储报告信息等。
安装MySQL。
使用sql/coverage_report.sql 初始化MySQL信息
3.3.2 配置Super-jacoco服务application.properties信息
在下载下来的项目中,application.properties配置信息
src/main/resources/application.properties
# 以下信息需要手动配置
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/super-jacoco?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
#git username & password
gitlab.username=miaojiang
gitlab.password=mj123456
主要配置
数据库的连接信息:url 账号 密码
gitlab 的账号、密码
注意:
数据库的驱动配置,5.x与8.x 版本的MySQL,驱动是不一致的,根据自己的数据库配置。
如果你的数据库为MySQL 8.x,需要修改项目中的驱动,具体修改方式在第二章有写,上面翻一番
3.3.3、编译super jacoco项目
修改完super jacoco项目中的配置文件,就可以编译该项目了。生成super jacoco.jar
3.3.4、部署 super jacoco 服务
启动super-jacoco.jar 服务
执行命令
nohup java -jar super-jacoco.jar &
默认端口为8899
可以8899端口,程序是否启动成功
lsof -i:8899
注意:
在部署 super jacoco 服务的机器上,一定要有以下环境:
1、有mvn环境,如果没有的话,需要配置
2、有git环境,并配置了能拉取被测项目的gitlab账号和密码。
可以自己在这台机器上手动试一下,是否有权限
git clone xxx.xxx.git
3、启动super jacoco 服务的机器,一定要有访问被测服务器18513端口的权限。
公司中对服务器权限、端口权限控制的比较严,可能需要走工单进行申请,这个需要注意。
3.4、代码覆盖率收集
假如:
super jacoco 服务部署在了 123.123.123.123 这台服务上了
被测项目mock-server 部署在了 456.456.456.456 机器上了
3.4.1 启动代码覆盖率收集
1)启动覆盖率收集URL:
http://123.123.123.123 /cov/triggerEnvCov
调用方法:POST参数(body方式传入):
{
"uuid": "uuid",
"type": 1,
"gitUrl": "git@git",
"subModule": "",
"baseVersion": "master",
"nowVersion": "master",
"address": "456.456.456.456",
"port": "18513"
}
备注:
IP和port为被测服务所在服务器的IP和端口
uuid: 自定义的一个id,将来拉取覆盖率时,通过这个uuid来拉取
gitUrl: 被测项目的gitlab地址
返回:
{
"code": 200,
"data": true,
"msg": "msg"
}
3.4.2 获取代码覆盖率
覆盖率结果URL:
http://123.123.123.123/cov/getEnvCoverResult
调用方法:
GET
参数:
uuid(String)
返回:
{
"code": 200,
"data": {
"coverStatus": 1,
"errMsg": "msg",
"lineCoverage": 100.0,
"branchCoverage": 100.0,
"logFile": "file content",
"reportUrl": "http://"
},
"msg": "msg"
}
四、报错处理
4.1 "errMsg": "统计失败:编译代码出错"
背景:
拉取报告时 提示 ,统计失败:编译代码出错
原因:
查看启动super jacoco 服务的日志:
2023-10-12 17:01:14,642 INFO (CodeCloneExecutor.java:45)- uuid miaojiang开始下载代码...
2023-10-12 17:01:16,316 INFO (CodeCloneExecutor.java:52)- uuid miaojiang完成下载代码...
2023-10-12 17:01:16,322 DEBUG (BaseJdbcLogger.java:181)- ==> Preparing: UPDATE diff_coverage_report SET `request_status`=?, `err_msg`=?, `line_coverage`=?, `branch_coverage`=?, `report_url`=?, `diffmethod`=?, `log_file`=?, `now_local_path`=?, `update_time`=NOW() WHERE job_record_uuid=?
2023-10-12 17:01:16,323 DEBUG (BaseJdbcLogger.java:181)- ==> Parameters: 102(Integer), (String), -1.0(Double), -1.0(Double), (String), (String), http://127.0.0.1:8899/logs/miaojiang.log(String), /Users/zhaohui/app/super_jacoco/clonecode/miaojiang/master(String), miaojiang(String)
2023-10-12 17:01:16,327 DEBUG (BaseJdbcLogger.java:181)- <== Updates: 1
2023-10-12 17:01:16,328 DEBUG (BaseJdbcLogger.java:181)- ==> Preparing: UPDATE diff_coverage_report SET `request_status`=?, `err_msg`=?, `line_coverage`=?, `branch_coverage`=?, `report_url`=?, `diffmethod`=?, `log_file`=?, `now_local_path`=?, `update_time`=NOW() WHERE job_record_uuid=?
2023-10-12 17:01:16,329 DEBUG (BaseJdbcLogger.java:181)- ==> Parameters: 3(Integer), (String), -1.0(Double), -1.0(Double), (String), (String), http://127.0.0.1:8899/logs/miaojiang.log(String), /Users/zhaohui/app/super_jacoco/clonecode/miaojiang/master(String), miaojiang(String)
2023-10-12 17:01:16,331 DEBUG (BaseJdbcLogger.java:181)- <== Updates: 1
IP:127.0.0.1
2023-10-12 17:01:16,335 INFO (CmdExecutor.java:43)- CmdThreadPool:java.util.concurrent.ThreadPoolExecutor@4b5dade5[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
2023-10-12 17:01:16,335 INFO (CmdExecutor.java:48)- executeCmd : bash -c cd /Users/zhaohui/app/super_jacoco/clonecode/miaojiang/master&&mvn clean compile >>/Users/zhaohui/report/logs/miaojiang.log
2023-10-12 17:01:16,346 INFO (CmdExecutor.java:101)- bash: mvn: command not found
2023-10-12 17:01:16,347 INFO (CmdExecutor.java:56)- readLine.stop();
2023-10-12 17:01:16,347 INFO (CmdExecutor.java:58)- progressBar.stop();
2023-10-12 17:01:16,348 INFO (CmdExecutor.java:59)- executeCmd done !!!!!!
2023-10-12 17:01:16,348 INFO (CmdExecutor.java:60)- worker done !!!!!! times = 0s
2023-10-12 17:01:16,349 DEBUG (BaseJdbcLogger.java:181)- ==> Preparing: UPDATE diff_coverage_report SET `request_status`=?, `err_msg`=?, `line_coverage`=?, `branch_coverage`=?, `report_url`=?, `diffmethod`=?, `log_file`=?, `now_local_path`=?, `update_time`=NOW() WHERE job_record_uuid=?
2023-10-12 17:01:16,350 DEBUG (BaseJdbcLogger.java:181)- ==> Parameters: 203(Integer), 编译代码出错(String), -1.0(Double), -1.0(Double), (String), (String), http://127.0.0.1:8899/logs/miaojiang.log(String), /Users/zhaohui/app/super_jacoco/clonecode/miaojiang/master(String), miaojiang(String)
2023-10-12 17:01:16,352 DEBUG (BaseJdbcLogger.java:181)- <== Updates: 1
2023-10-12 17:01:16,352 INFO (CodeCovServiceImpl.java:252)- Thread-4计算覆盖率具体步骤...编译失败uuid=miaojiang
2023-10-12 17:01:16,353 INFO (CodeCovServiceImpl.java:309)- Thread-4计算覆盖率具体步骤...编译失败uuid=miaojiang
2023-10-12 17:01:19,981 DEBUG (BaseJdbcLogger.java:181)- ==> Preparing: SELECT * from diff_coverage_report WHERE `request_status`=? AND `from`=? ORDER BY `update_time` asc limit ?
2023-10-12 17:01:19,987 DEBUG (BaseJdbcLogger.java:181)- ==> Parameters: 0(Integer), 1(Integer), 1(Integer)
根据日志信息,可以看出发生了以下事件:
17:01:14 - miaojiang开始下载代码。
17:01:16 - miaojiang完成下载代码。
17:01:16 - 执行数据库更新操作,更新了diff_coverage_report表的一行数据。
17:01:16 - 执行数据库更新操作,再次更新了diff_coverage_report表的一行数据。
17:01:16 - 从IP地址为127.0.0.1的主机上执行命令:cd /Users/zhaohui/app/super_jacoco/clonecode/miaojiang/master&&mvn clean compile >>/Users/zhaohui/report/logs/miaojiang.log,但是出现了错误:bash: mvn: command not found。
17:01:16 - 终止读取日志和进度条。
17:01:16 - 执行命令结束,执行时间为0秒。
17:01:16 - 再次执行数据库更新操作,更新了diff_coverage_report表的一行数据,将request_status设为203,err_msg设为"编译代码出错"。
17:01:16 - Thread-4处理计算覆盖率的具体步骤时发生编译失败,uuid为miaojiang。
17:01:19 - 执行数据库查询操作,从diff_coverage_report表中选取request_status为0,from为1的记录,并按update_time升序排列,限制返回结果数量为1个。
注意:
bash: mvn: command not found。
说明 127.0.0.1 服务上,没有配置mvn环境
解决办法:
在127.0.0.1 机器上,配置mvn环境
4.2 编译失败 (权限相关)
编译失败,还有一种可能是权限问题,在执行 mvn clean compile 时,可能需要拉取一些公司的内部资源,如果没有权限的话,依然会编译失败
4.3 编译失败 (网络慢)
编译过程中,在官方仓库拉取mvn资源时(repo.maven.apache.org),网络原因,下载速度非常慢,导致编译失败。
我们可以给mvn settings.xml 配置一个国内的镜像,加速一下。
找到mvn地址
echo $M2_HOME
找到这个路径下的/conf/settings.xml 文件。
在 Maven 的 settings.xml 文件中可以设置镜像站点,在 <mirrors> 标签中添加一个新的镜像即可。例如:
<mirrors>
<mirror>
<id>alimaven</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>
4.4 编译失败
报错:
2023-10-19 10:32:12,387 INFO (CmdExecutor.java:48)- executeCmd : bash -c cd /root/app/super_jacoco/clonecode/101901/master&&java -jar /root/org.jacoco.cli-1.0.2-SNAPSHOT-nodeps.jar dump --address 10.110.10.100 --port 18513 --destfile ./jacoco.exec 2023-10-19 10:32:12,393 INFO (CmdExecutor.java:101)- Error: Unable to access jarfile /root/org.jacoco.cli-1.0.2-SNAPSHOT-nodeps.jar
解决办法:
手动将super jacoco 项目中jacoco/org.jacoco.cli-1.0.2-SNAPSHOT-nodeps.jar 文件,复制到服务器的/root目录下
五、Super-jacoco原理
5.1 整体流程
为了支持增量覆盖率收集,我们需要做两件事情:**1)**获取不同版本代码diff文件;**2)**对jacoco进行二次开发,使其支持增量方法列表参数。
5.2 获取增量代码
主要流程:拉取master(参照分支)和feature(提测分支)代码,再通过JGit对两个分支源码进行比对,获取增量代码。以下为部分代码片段:
5.3. jacoco 二次改造,支持增量方法列表参数
JaCoCo 对 exec 的解析主要是在 Analyzer 类的 analyzeClass(final byte[] source) 方法。这里面会调用 createAnalyzingVisitor 方法,生成一个用于解析的 ASM 类访问器,继续跟代码,发现对方法级别的探针计算逻辑是在 ClassProbesAdapter 类的 visitMethod 方法里面。所以我们只需要改造 visitMethod 方法,使它只对提取出的每个类的新增或变更方法做解析,非指定类和方法不做处理。改造后的核心代码片段如下:
5.4. 执行
只需要在执行的mvn命令中加入-Djacoco.diffFile=变更方法列表,即可收集变更方法的代码覆盖率。如果不传入-Djacoco.diffFile或者Djacoco.diffFile参数为空,则默认收集全量覆盖率。
5.5. 报告输出
覆盖率报告如下图,在图中是某个 service 的实现类,在最新的代码中有23个方法,但是只会对变更或新增的5个方法进行覆盖率统计与显示:
5.6、架构
底层架构
六、相关资料:
滴滴开源Super-jacoco:java代码覆盖率收集平台 - 掘金
七、代码覆盖率收集 (使用说明)
在super jacoco的基础上,进行了二次开发,集成到了测试平台。
这里简单说一下使用方法:
数据是5分钟自动拉取一次,如果已经创建过任务,不需要再次创建任务。
项目重启之前,创建一个任务。重启项目后,再创建新的任务:
旧的任务会合并新的数据。新的任务并不会合并之前的报告。
7.1 正常场景
整体顺序是:
1、先启动被测应用
2、执行手工测试
3、创建任务1(接口:/cov/triggerEnvCov)
4、拉取报告(接口:/cov/getEnvCoverResult?uuid=任务1)
5、执行的手工测试被成功收集
注意:
先执行手动测试,还是先创建任务,先后是没有顺序的,都可以的。
7.2 被测项目重启的场景
1、项目启动
2、创建任务001(接口:/cov/triggerEnvCov)
3、执行手工测试(A模块)
4、重启被测项目
5、创建任务002
6、执行手工测试(B模块)
查看数据(最好等5分钟之后,因为数据5分钟自动拉取一次)
任务001:包含A模块与B模块的数据
任务002:只包含B模块的数据
7.3 被测项目(新增了代码)重启的场景
1、项目启动
2、创建任务001(接口:/cov/triggerEnvCov)
3、执行手工测试(A模块)
4、重启被测项目,并新增了C模块的代码
5、创建任务002
6、执行手工测试(B模块与C模块的数据)
查看数据(最好等5分钟之后,因为数据5分钟自动拉取一次)
任务001:包含A模块与B模块的数据
任务002:包含B模块与C模块的数据
7.4 增量代码覆盖
研发在master上拉了一个dev分支。在dev分支上开发了一些新的功能。
我只想要统计dev分支上新增的代码覆盖率,而不是dev分支所有的代码覆盖率。
创建任务:
{
"uuid": "test_new_develop",
"type": 2,
"gitUrl": "http://xxxx.git",
"subModule": "",
"baseVersion": "master",
"nowVersion": "develop",
"address": "127.0.0.1",
"port": "18513"
}
type:2 代表统计增量代码覆盖率
baseVersion:master 最原始的分支,用来被比较的
"nowVersion": "develop" 研发新增代码的分支
八、报告解读:
条件覆盖
红钻:表示未覆盖
黄钻:表示部分覆盖
绿钻:表示全部覆盖
注:条件覆盖都是用钻表示。
Lines覆盖
红色背景:无覆盖,该行的所有指令均无执行。
背景:部分覆盖,该行部分指令被执行。
绿色背景:全覆盖,该行所有指令被执行。
九、注意事项:
9.1 被测项目启动命令:
nohup java -javaagent:/usr/web/cs_super_jacoco/org.jacoco.agent-0.8.5-runtime.jar=includes=*,output=tcpserver,port=18513,address=,append=true -jar com_ky.jar > /dev/null 2>&1 &
注意:
includes=* ,这里一定要使用星号哦,不要忘了