前言
编写单元测试是开发健壮程序的有效途径,单元测试写的好不好可以从多个指标考量,其中一个就是单元测试的覆盖率。单元测试覆盖率可以看到我们的单元测试覆盖了多少代码行、类、分支等。查看单元测试覆盖率可以使用一些工具帮助我们计算,如 idea 自带的单元测试工具、jacoco 等。jacoco 是一款开源的单元测试工具,可以方便地整合到 maven 中,提供了更丰富的配置及通过规则,可以生成单元测试报告,也可以方便地和自动化流水线整合。所以更推荐使用 jacoco 作为单元测试工具。
配置
maven 配置
可以通过 maven 插件的方式整合 jacoco,这样使用 mvn test
命令就可以生成 jacoco 的测试报告了。
要配置 jacoco,在 build 中添加 jacoco 插件即可。
<build>
<plugins>
<!-- jacoco -->
<plugin>
<groupid>org.jacoco</groupid>
<artifactid>jacoco-maven-plugin</artifactid>
<configuration>
<!-- 排除文件 -->
<excludes>
<exclude>**/*test.class</exclude>
<exclude>**/*configuration.class</exclude>
<exclude>**/*properties.class</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>jacoco-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>jacoco-report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<!--定义输出的文件夹-->
<outputdirectory>target/test-report</outputdirectory>
<footer>火眼9988</footer>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
上面定义了两个执行,prepare-prepare-agent 是准备 jacoco 的运行时代理,而 jacoco-report 则是生成报告。
然后执行 mvn test
或通过 ide 的 maven 面板执行测试,完成后就会在指定目录下生成 jacoco 的报告。默认会生成 csv 和 html 格式的,csv 可以用于自动化流水线整合,而 html 的可以用浏览器打开查看。
定义通过规则
我们可以使用 jacoco 方便地配置通过规则,只有满足特定规则才能通过测试,否则 test 执行会失败。
添加一个 execution 即可,在 rule 中定义我们的通过规则。
<execution>
<id>jacoco-check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>bundle</element>
<excludes>
<exclude>**/*test.class</exclude>
<exclude>**/*configuration.class</exclude>
<exclude>**/*properties.class</exclude>
</excludes>
<limits>
<limit>
<counter>line</counter>
<value>coveredratio</value>
<minimum>0.8</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
上面定义了一条规则,包含一个限制:行覆盖率最低为 80%,这里可查看完整案例。
其中 rule 标签定义了一条规则,element 定义了规则对应的范围,可选值有:
- bundle:表示整个模块,是默认值
- package:表示代码包
- class:表示类
- sourcefile:源文件
- method:表示方法
每个规则可以定义多条限制(limit),每个限制有一个特定的指标:
- instruction:字节码指令,是最细粒度的指标,默认值
- line:代码行,一行代码可能有多个字节码指令
- branch:分支,if 或 switch 包含了多个分支
- complexity:圈复杂度,是代码复杂度的衡量标准,简单来说越大越复杂,需要的测试用例越多,详细算法可参考百科
- method:方法
- class:类
为每个指标的值定义一个最大值或最小值。
- totalcount:总数
- coveredcount:覆盖的数量
- missedcount:未覆盖的数量
- coveredratio:覆盖率,范围从 0.0 到 1.0, 默认值
- missedratio:未覆盖率,范围从 0.0 到 1.0
excludes 标签定义要排除的文件。
例如,下面的规则定义了 instruction 的覆盖率至少 80%,且没有类未被覆盖。
<rules>
<rule>
<element>bundle</element>
<limits>
<limit>
<counter>instruction</counter>
<value>coveredratio</value>
<minimum>0.80</minimum>
</limit>
<limit>
<counter>class</counter>
<value>missedcount</value>
<maximum>0</maximum>
</limit>
</limits>
</rule>
</rules>
规则的详细配置可参考官方文档。
idea ide 配置
idea ide 除了支持自带的单元测试工具外,也支持使用 jacoco。在配置中搜索 jacoco
,java 覆盖率中选择运行程序为 jacoco 即可(不同的版本可能略有差别)。
配置后在 ide 中执行单元测试时,选择第三项“使用覆盖率运行…”,运行完成后即可打开覆盖率面板,可查看当前单元测试的覆盖率,代码编辑器中也会以颜色标记。
排除测试
如果在代码中使用 lombok,会有很多生成的代码,这些往往不需要测试覆盖,但如果不覆盖的话会影响测试覆盖率。jacoco 可以自动排除 lombok 的 @generated 注解标记的类或方法。要让 lombok 为生成的代码添加注解,可在项目的根目录中添加配置文件 lombok.config
。
然后添加一行配置。
lombok.addlombokgeneratedannotation = true
这样生成的代码就会带上 @generated 注解,被 jacoco 排除计算了。当然,如果自己的代码不用测试,又不想影响覆盖率,也可以添加这个注解。这里可以查看实际案例。
多模块聚合报告
如果我们的项目有多个模块,jacoco 会在每个模块下生成一个单独的报告,能不能生成一个聚合的报告呢?jacoco 也贴心地为我们提供了生成聚合报告的方法。
首先添加一个模块,专门用于生成聚合报告,可以只有一个 pom.xml 文件。
在该模块的 pom 中添加如下配置,report-aggregate 用于生成聚合的报告。
<build>
<plugins>
<plugin>
<groupid>org.jacoco</groupid>
<artifactid>jacoco-maven-plugin</artifactid>
<executions>
<execution>
<id>jacoco-report-aggregate</id>
<phase>test</phase>
<goals>
<goal>report-aggregate</goal>
</goals>
<configuration>
<outputdirectory>target/test-report</outputdirectory>
<footer>火眼9988</footer>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
也不要忘记在 pom 的 dependencies 中添加其他模块作为依赖。这里可查看完整案例。
这样就可以获得我们项目的整体单元测试覆盖率了哦。
理解报告
如果能理解规则定义,报告就非常容易理解了。如上面的截图所示,报告表格各个列的意思为:
- element:对应配置的 element,例如 bundle 则是模块名
- missed instructions cov:字节码指令覆盖率(下面的数字是未覆盖数量 of 总数)
- missed branches cov:分支覆盖率(下面的数字是未覆盖数量 of 总数)
- missed / cxty:未覆盖的以及总的圈复杂度
- missed / lines:未覆盖的以及总的行数
- missed / methods:未覆盖的以及总的方法
- missed / classes:未覆盖的以及总的类
详情中可以看到每个文件的覆盖情况。其中,行的颜色代表的当前行的覆盖情况。红色代表未覆盖,黄色代码部分覆盖(分支条件没有全部执行到),绿色代码已覆盖。
赶紧整合试试吧!
发表评论