Jacoco代码覆盖率报告分析
Jacoco代码覆盖率报告分析
Jacoco代码覆盖率统计维度、报告字段说明、报告详细分析描述。并为精准测试、健壮性测试提供指导。
Jacoco代码覆盖率统计维度
Jacoco是从代码指令(Instructions, C0coverage),分支(Branches, C1coverage),圈复杂度(Cyclomatic Complexity),行(Lines),方法(Methods),类(Classes)等维度进行分析的。
Jacoco生成报告字段说明
字段名称 | 说明 | 描述 |
---|---|---|
Element | 元素 | 最外层展示分组名称,依次为包>类>方法 |
Missed Instructions Cov. | 指令覆盖,字节码中指令 | 1. Jacoco最小计数单元是单个Java二进制指令代码。 2. 指令覆盖率提供了代码是否被执行的信息。 |
Missed Branches Cov. | 分支覆盖率 | 1. Jacoco计算分支覆盖率,包括所有的if和switch语句。 2. 用钻石表示,分支覆盖不能看行。 3. 这是度量一个方法里面的总分支数,确定执行和不执行的分支数量。 4. 分支覆盖率总是可用的,即使class文件里面没有调试信息。 注意:异常处理时不在分支度量里面统计的。 |
Missed Cxty | 圈复杂度 | 1. Jacoco为每一个非抽象方法计算复杂度,最终计算出类、包和组的复杂度。由McCabe1996可知圈复杂度的定义是,在(线性)组合中,计算在一个方法里面所有可能路径的最小数目。 2. 复杂度可以作为度量单元测试是否完整覆盖所有场景的一个依据,复杂度即使在没有调试信息的情况下也可以计算。 3. 圈复杂度V(G)的正式定义是基于方法的控制流图的有向图表示的:V(G)=E-N+2 E是边界数量,N是节点的数量。 4. Jacoco的复杂度计算方程:V(G)=B-D+1 B是分支的数量,D是决策点的数量。 5. 基于每个分支的覆盖情况,Jacoco也为每个方法计算覆盖和缺失的复杂度,缺失的复杂度同样表示测试案例没有完全覆盖到这个模块。 注意:Jacoco不将异常处理作为分支,try/catch块不增加复杂度。 |
Missed Lines | 代码行 | 1. class文件使用debug信息编译后,就可以统计行的覆盖率信息。一行源码是否被执行,要看这一行中是否至少有一个指令被执行。 2. 一行代码一般被编译成多个二进制代码指令。 |
Missed Methods | 方法 | 每一个非抽象方法至少包含一个指令,一个方法是否执行取决于方法中是否有至少一个指令被执行。 在Jacoco中,构造器和静态初始化同样会像方法一样统计,其中一些方法可能没有可以直接对应的源码,比如默认构造器或常量的初始化命令。 |
Missed Classes | 类 | 一个方法是否执行取决于类中是否至少有一个方法被执行。 注意:Jacoco认为构造器和静态初始化都是方法,Java的接口一般包含静态初始化,所以接口也同样被认为是可执行的类。 |
注:Missed表示未覆盖
Jacoco生成报告详细分析
将生成的Jacoco报告打开,如何生成Jacoco的报告,后续会介绍,本篇主要介绍Jacoco报告的分析。为了更好的分析Jacoco覆盖率,新建了一个工程进行演练。
Github:https://github.com/jinglv/spring-boot-demo
包
展示当前所有的包,包的覆盖率取决于类的覆盖情况。
类
点进去一个包中,显示该包下所有的类,类的覆盖率取决于方法的覆盖情况。
方法
点进去一个类中,显示该类下所有的方法名,方法的覆盖率取决于方法内的覆盖情况。
Jacoco覆盖率的详细分析
以上已经介绍了Jacoco代码覆盖统计的维度,接下来针对于实例,进行报告中字段的详细说明。
Missed Instructions
说明:红色进度条表示未覆盖,绿色进度条表示已覆盖,Cov为总体覆盖率。
Total:1176表示没有覆盖的指令,1517表示总的指令
Cov:表示覆盖率
Jacoco计算的最小单位就是字节码指令。指令覆盖表明了在所有的指令中,哪些被执行过以及哪些没有被执行。这项指数完全独立与源码格式并且在任何情况下有效,不需要类文件的调试信息。
Missed Branches
说明:红色进度条表示未覆盖,绿色进度条表示已覆盖,Cov为总体覆盖率。
Total:168表示没有覆盖的分支,172表示总的分支
Cov:表示覆盖率
从上面的图看出Missed Instructions(指令)覆盖率为22%,Missed Branches(分支)覆盖率为2%,为什么会是这样的情况呢?原因是:代码行覆盖的并不代表分支都已覆盖。
实例分析:
从上图看出,ofException这个方法,指令覆盖了85%,分支覆盖了50%,这时进入到方法中查看:
查看该方法调用的代码看,只返回了ApiCodeEnum.FAIL.getCode(),说明该条件只执行了结果为true的情况,没有执行结果为false的情况,因此指令覆盖和分支覆盖都不全。
Missed Cxty
Total:166表示没有覆盖到的圈复杂度,213表示总共的圈复杂度
实例分析:进入到包service.impl包下,再进入CalcServiceImpl实例类中
查看该实现类里,有加减乘除四个方法,圈复杂度的显示加减乘都是1,除是2,接下来再进入的方法内再次查看:
从圈复杂度的介绍可知,圈复杂度是根据代码中判定条件的节点来计算的,jacoco在统计代码时也是根据代码中有多少个判定节点来算(代码里有多少if、for、while…的判定节点来统计的),从上面的代码来看,add、sub、mul这三个方法,没有判定节点,计算是0+1,因此这个三个方法都是复杂度都是1,div方法中有一个if节点,计算是1+1,这个方法的复杂度就是2,和jacoco的报告就对上了。
将上面的代码,进行修改并执行
查看jacoco的报告
由于在代码中添加了if的判定条件,统计的圈复杂度明显增加,通过计算公式的计算也是一致的。
根据官网介绍,jacoco的圈复杂度的计算公式是:V(G) = B - D + 1
B是分支数量,D是决策点数量,查看一些资料后,并不理解这个分支数量和决策点数量具体指什么,因此还是根据圈复杂度里介绍的计算公式更容易理解一些。
Missed.Lines、Methods、classes
Lines:代码行,共有177行,98行未覆盖到(例如:else是不统计到Lines的)
Methods:方法,共有127个方法,80个未覆盖到
Classes:类,共有18个类,4个未覆盖到
Missed表示未覆盖到
覆盖率标识
钻石代表分支覆盖情况
- 红色钻石:这一行没有分支被执行
- 黄色钻石:这一行中只有部分分支被执行
- 绿色钻石:这一行的所有分支都被执行
背景颜色代表指令覆盖率
- 红色背景:这一行并没有任何指令被执行
- 黄色背景:这一行的部分指令被执行
- 绿色背景:这一行的所有指令都被执行了