Jacoco
1、什么是jacoco
jacoco是一个免费、开源java代码覆盖率工具。
2、什么是代码覆盖率
覆盖率是用来衡量测试代码对功能代码的测试情况,通过统计测试中对功能代码中行、分支、类等模拟场景数量,来量化说明测试的充分度。代码覆盖率=代码的覆盖程度,一种度量方式
覆盖率简单说:跑了一个测试用例,项目代码中哪些模块、文件、类、方法、行 执行了。
其中行覆盖率是最细粒度,其他覆盖率可以从行覆盖率情况计算出来。
1、行覆盖
当至少一个指令被指定源码行执行时,该源码被认为已执行。
2、分支覆盖
if和switch语句算作分支覆盖率,这个指标计算一个方法中的分支总数,并决定已执行和未执行的分支的数量
全部未覆盖:所有分支均未执行,红色标志
部分覆盖:部分分支被执行,黄色标志
全覆盖:所有分支均已执行,绿色标志
3、方法覆盖
当方法中至少有一个指令被执行,该方法被认为已执行,包括构造函数和静态初始化方法。
4、类覆盖
当一个类至少有一个方法已执行,则该类被认为已执行,包括构造函数和静态初始化方法。
3、代码覆盖率意义
分析未覆盖部分的代码,反推测试设计是否充分,没有覆盖到的代码是否存在测试设计盲点。
4、覆盖率的误区
若代码如下:
if (i>100)
j = 10 / i // 没有除零错误
else
j=10 / (i + 2) // 1==-2除零错误
覆盖两个分支只需i101和i1,但对于i==-2没有作用。
所以:
- 不要简单的追求高的代码覆盖率
- 高覆盖率测试用例不等于测试用例有效
- 没覆盖的分支相当于该分支上的任何错误肯定都测不到
5、jacoco原理
jacoco使用插桩的方式来记录覆盖率数据,是通过一个probe探针来注入。
Jacoco是通过修改class文件的字节码来进行代码覆盖率统计。在原有class字节码中的指定位置插入探针字节码,形成新的字节码指令流。jacoco使用的是ASM字节码框架对字节码进行修改的。jacoco的探针实际是一个布尔值,当代码执行到探针位置时,将其置为true,该探针前面的代码会被认为执行过,然后对该部分代码对应的html文件中的css样式进行染色(红色表示未覆盖,绿色表示已覆盖,黄色表示部分覆盖),形成最终的覆盖率报告。
插桩模式有两种:
1、on-the-fly模式
JVM中通过-javaagent参数指定特定的jar文件启动Instrumentation的代理程序,代理程序在通过Class Loader装载一个class前判断是否转换修改class文件,将统计代码插入class,测试覆盖率分析可以在JVM执行测试代码的过程中完成。
2、offline模式
在测试之前先对文件进行插桩,生成插过桩的class或jar包,测试插过桩的class和jar包,生成覆盖率信息到文件,最后统一处理,生成报告。
3、on-the-fly和offline对比
on-the-fly更方便简单,无需提前插桩,无需考虑classpath设置问题。
存在一下情况不适合使用on-the-fly模式:
-
不支持javaagent
-
无法设置JVM参数
-
字节码需要被转换成其他虚拟机
-
动态修改字节码过程和其他agent冲突
-
无法自定义用户加载类
6、jacoco应用
1、下载jacoco
官网:https://www.jacoco.org/jacoco/index.html
2、拷贝jar包
3、启动jacocoagent
打开cmd窗口启动项目
java -javaagent:jacocoagent.jar=includes=*,output=tcpserver,port=6300,address=localhost,append=true -jar demo-001.jar
# 使用-javaagent启动,includes在哪里插桩,output覆盖率以什么方式输出,demo-001.jar是被测项目的jar包
4、cli包dump生成exec文件
重新打开cmd窗口,生成exec文件
java -jar jacococli.jar dump --address 127.0.0.1 --port 6300 --destfile jacoco-demo.exec
# --address 127.0.0.1 --port 6300 指向jacocoagent启动IP和端口
# jacoco-demo.exec为生成exec文件名
5、cli包exec生成report报表
java -jar jacococli.jar report jacoco-demo.exec --classfiles D:\code\devops\SBD\target\classes --sourcefiles D:\code\devops\SBD\src\main\java --html html-report --xml report.xml --encoding=utf-8
# --sourcefiles 和 --classfiles 是本地被测项目源码和字节码路径
6、覆盖率报告
- Instructions:Java 字节指令的覆盖率。执行的最小单位,和代码的格式无关。
- Branches:分支覆盖率。注意,异常处理不算做分支。
- Cxty(Cyclomatic Complexity):圈复杂度, Jacoco 会为每一个非抽象方法计算圈复杂度,并为类,包以及组(groups)计算复杂度。圈复杂度简单的说就是为了覆盖所有路径,所需要执行单元测试数量,圈复杂度大说明程序代码可能质量低且难于测试和维护。
- Line:行覆盖率,只要本行有一条指令被执行,则本行则被标记为被执行。
- Methods:方法覆盖率,任何非抽象的方法,只要有一条指令被执行,则该方法被计为被执行。
- Classes:类覆盖率,所有类,包括接口,只要其中有一个方法被执行,则标记为被执行。注意:构造函数和静态初始化块也算作方法。
钻石代表分支覆盖情况:
- 红钻:这一行没有分支被执行;
- 黄钻:这一行中只有部分分支被执行;
- 绿钻:这一行的所有分支都被执行;
背景颜色代表指令覆盖率
-
红色背景:这一行并没有任何指令被执行;
-
黄色背景:这一行的部分指令被执行;
-
绿色背景:这一行的所有指令都被执行了;
7、jacoco增量覆盖
1、增量覆盖
增量覆盖:两次提交之间有哪些代码或者分支没有被覆盖。
目的:检测同一个测试用例在修改前后代码上的行覆盖情况。
假设两次提交代码变更如下:
if (x > 100)
- print("apple");
+ print("lemon");
else
- print("12345");
+ print("上山打老虎");
每行代码有三种状态:+ - 不变
修改前后跑同一个用例,每行有四种状态:修改前覆盖、修改前未覆盖,修改后覆盖、修改后未覆盖。
所以增量覆盖率总共有3*4 = 12种情况,比较重要有:新增代码没有覆盖;新增代码覆盖了;不变的代码修改前覆盖,修改后未覆盖等等。
2、增量应用
增量原理
计算增量代码具体步骤:
- 计算出两个版本的差异代码
- 将差异代码在report阶段传给jacoco
- 修改jacoco源码,生成报告时判断代码是否是增量代码,只有增量代码才去生成报告
jacoco二开:https://gitee.com/Dray/jacoco.git
增量代码获取:https://gitee.com/Dray/code-diff.git
使用方法:
1、jacoco客户端收集信息
java - javaagent:jacocoagent.jar=includes=*,output=tcpserver,port=6300,address=localhost,append=true -jar demo-001.jar
2、二开cli包生成exec文件
java -jar cli-0.8.7-diff.jar dump --address 127.0.0.1 --port 6300 --destfile jacoco-demo.exec
3、获取两次提交代码差异
java -jar code-diff.jar # 启动diff项目
# 网址:http://127.0.0.1:8085/doc.html
4、二开cli包生成report增量报表
java -jar cli-0.8.7-diff.jar report jacoco-demo.exec --classfiles D:\code\devops\SBD\target\classes --sourcefiles D:\code\devops\sBD\src\main\java --html html-report-diff --xml report-diff.xml --diffcode "[{\ "c1assFi1e\":\"me/xz/contro11er/Usercontro1ler\",\"lines\":[{\"endLineNum\":74,\"startLineNum\":69,\"type\":\"INSERT\"}],\"methodInfos\":[{\"methodName\":\"login\",\"parameters\":\"User\"}],\"moduleName\":\"src\",\"type\":\"MODIFY\"}]" --encoding=utf-8
3、增量实战,整合jenkins
1、拉取被测代码
2、启动jacocoagent
pipeline {
agent any
environment {
//jacocoagent.jar、code-diff.jar、c1i-0.8.7-diff.jar // 下载地址
agent_ur1 = "https://gitee.com/dzitcast/jacoco-c1i-diff.git"
//jenkins git token
git_token = "gitee"
//被测项目jar名称
jar_name = "springbootdemo-0.0.1-SNAPSHOT.jar"
//被测项目名
project_name = "springbootdemo";
}
stages {
stage("下载最新被测代码"){
steps{
build 'springbootdemo'
}
}
stage('获取jacoco agent jar包'){
steps{
script{
agent_file = "${env.WORKSPACE}/jacocoagent.jar"
diff_file = "${env.WORKSPACE}/code-diff.jar"
c1i_file = "${env.WORKSPACE}/c1i-0.8.7-diff.jar"
//可加可不加
if(fileExists(agent_file) & fileExists(diff_file) & fi1eExists(cli_file)) {
echo("jar存在")
}e1se {
echo( "jar不存在,git拉取jar")
git credentialsId: "${git_token}", ur1: "${agent_ur1}"
}
}
}
}
stage('启动jacocoagent&启动code-diff'){
steps{
script{
withEnv(['JENKINS_NODE_COOKIE=background_job']) {
sh "nohup java -javaagent:jacocoagent.jar=inc1udes=*,output=tcpserver,port=6300,address=loca1host,append=true -jar ${JENKINS_HOME}/workspace/${project_name}/target/${jar_name} &"
sh "nohup java -jar code-diff.jar &"
}
}
}
}
}
}
3、生成报告
pipeline {
agent any
environment {
//增量代码项目接口地址
diff_ur1 = "http://8.129.52.205:8085"
//被测代码git地址
gitur1 = "https://gitee.com/dzitcast/springbootdemo.git"
//比较git版本号
baseVersion = "a12beeldee54aa5a2a18d99e43eaec696f5b8931"
//当前git版本号
nowVersion = "871fe6ef7942efb1884dc1aa3b0d9b16d1357034"
//git用户名和密码
password = "123123"
username = "123123"
//jacocoagent项目名,加载jar使用
jacoco_project_name = "jacoco增量-agent"
//被测项目名,加载源码和c1ass文件
test_project_name = "sbd"
//二开jar包名
cil_jar = "c1i-0.8.7-diff.jar"
//读取文件
diffcode = readFile encoding: 'utf-8 ', file: 'uniqueData'
}
stages{
stage('获取增量字符串并保存到本地文件'){
steps{
script{
sh "cur1 -x GET '${diff_ur1}/api/code/diff/git/list?baseversion=${baseVersjon]&gitUr1=${gitUr1]&nowVersion=${nowVersionj&password=${password}&username=${username}’ -H 'Request-Origion:SwaggerBootstrapUi' -H 'accept:*/*’-H 'TOKEN:' | awk -v FS=' \"uniqueData\":' '/uniqueData/{print \$2}' > uniqueData"
diffcode = readFile encoding: 'utf-8',file: 'uniqueData'
}
}
}
stage('生产exec文件'){
steps{
script{
sh "java -jar ${JENKINS_HOME]}/workspace/${jacoco_project_name}/${cil_jar} dump --address 127.0.0.1 --port 6300 --destfile jacoco-demo.exec"
}
}
}
stage('全量报表'){
steps{
script{
sh "java -jar ${JENKINS_HOME}/workspace/${jacoco_project_name}/${cil_jar} report jacoco-demo.exec --c1assfiles ${(JENKINS_HoME ]}/workspace//${test_project_name}/target/classes --sourcefi1es ${JENKINS_HOME}/workspace/${test_project_name]/src/main/java --htm1 htm1-report --xm1 report.xm1 --encoding=utf-8"
}
}
}
stage('增量报表'){
steps{
script{
sh "java -jar
${JENKINS_HOME] /workspace/${jacoco_project_name}/${cil_jar} report jacoco-demo.exec --c1lassfiles ${JENKINS_HOME}/workspace/${test_project_name}/target/classes --sourcefiles ${JENKINS_HOME}/workspace/${test_project_name}/src/main/java --html htm1-report-diff --xm1 report-diff.xm1 --encoding=utf-8 --diffcode ${diffCode]"
sh "tar -cvf report.tar htm1-report htm1-report-diff"
}
}
}
stage('发送邮件'){
steps{
emailext attachmentsPattern: 'report.tar ' , body:‘下载附件',subject:
'jacoco报告',to: '[email protected]'
}
}
}
}
4、杀死进程(code-diff和jacocoagent)
kill -9 $(ps ax | grep code-diff | grep -v grep | awk '{ print $1 }')
kill -9 $(ps ax l grep jacocoagent l grep -v grep | awk '{ print $1 }')
标签:覆盖率,代码,jar,jacoco,--,report,diff,Jacoco From: https://www.cnblogs.com/annewwang/p/17613469.html