本文对应源码:欢迎关注我的公众号nrsc,并在同名文章中获取本文对应源码。
1 先明确一下具体问题
当你的单元测试代码会改动字节码时(比如下面的代码),网上能查到的很多Springboot整合jacoco的文章可能都会存在问题。
@Test
public void testDemoMethod() throws Exception {
//属性1:要mock的静态常量所在类的类型
//属性2:要mock的静态常量名称
//属性3:要mock的静态常量所在类的类型(或要mock的静态常量所在的对象)
//属性4:要设置的值
MemberModifier.field(Demo1StaticConstant.class, "DEMO_CONSTANT").set(Demo1StaticConstant.class, "huawei");
String result1 = Demo1StaticConstant.demoMethod("huawei");
Assert.assertEquals(result1, "111");
String result2 = Demo1StaticConstant.demoMethod("xiaomi");
Assert.assertEquals(result2, "222");
}
我遇到的问题主要有如下两个:
Jacoco: For report generation the same class files must be used as at runtime
与该链接一致:https://stackoverflow.com/questions/33517805/jacoco-for-report-generation-the-same-class-files-must-be-used-as-at-runtime
java.lang.IllegalStateException: Cannot process instrumented class XXX 类. Please supply original non-instrumented classes.
与该链接一致:https://github.com/wso2/carbon-config/issues/10
2 解决方案1 -- 不太完美
这里先贴一下整合jacoco的代码,然后说一下问题。
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<configuration>
<excludes>
<exclude>com/nrsc/unit/test/study/util/SpringContextUtil.class</exclude>
<exclude>com/nrsc/unit/test/study/*.class</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>default-instrument</id>
<goals>
<goal>instrument</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<dataFile>target/jacoco.exec</dataFile>
<outputDirectory>target/jacoco-report</outputDirectory>
</configuration>
</execution>
<execution>
<phase>test</phase>
<id>default-restore-instrumented-classes</id>
<goals>
<goal>restore-instrumented-classes</goal>
</goals>
</execution>
</executions>
</plugin>
该方案出自于文章《单元测试框架和覆盖率统计原理简析》
(链接:https://www.51cto.com/article/705162.html)。
其实这篇文章在N年之前,我还在阿里工作的时候就看到过,但是当时我们做整合时只需要点点点就OK了,而且单测覆盖率基本都是直接通过Aone
来跑的,所以没做太细的研究。
我用上面这个方案发现会有如下问题:
- 本地想跑出单测覆盖率报告,无法通过
mvn test
命令,要使用mvn verify
(说实话我有点不适应) - 使用
mvn verify
可以跑出单测覆盖率报告,但是仍会报<font color = blue>第一节</font>中我说的第一个错误(看着有点不是很爽),如下图:
3 比较好的解决方案
该方案是无意间试出来的,但是确实可以比较完美的解决上面的两个问题。
贴一下具体的整合的代码:
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<configuration>
<!--skip: 设置为 false,表示不跳过 JaCoCo 的执行-->
<skip>false</skip>
<!--指定存储覆盖率数据的 .exec 文件位置-->
<dataFile>${project.basedir}/jacoco.exec</dataFile>
<!--指定生成的覆盖率数据文件的位置,通常存储在构建目录中-->
<destFile>${project.build.directory}/jacoco.exec</destFile>
<!--指定生成的报告输出目录的位置-->
<outputDirectory>${project.reporting.outputDirectory}/jacoco</outputDirectory>
<!--指定用于存储字节码类的目录-->
<classDumpDir>${project.build.outputDirectory}</classDumpDir>
<!--如果设置为 true,则在收集数据时将覆盖率数据追加到文件中,而不是覆盖。-->
<append>true</append>
<!--设置不进行代码覆盖率统计的代码,可以采用正则-->
<excludes>
<exclude>com/nrsc/unit/test/study/util/SpringContextUtil.class</exclude>
<exclude>com/nrsc/unit/test/study/*.class</exclude>
<exclude>com/**/*/AccountWalletRPCService.class</exclude>
<exclude>com/nrsc/unit/test/study/dto/**/*/</exclude>
</excludes>
</configuration>
<executions>
<!--会出现 Please supply original non-instrumented classes.-->
<!--
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
-->
<!--在 test 阶段之前执行,插桩代码以收集覆盖率数据-->
<execution>
<id>default-instrument</id>
<goals>
<goal>instrument</goal>
</goals>
</execution>
<!--在 test 阶段后执行,恢复原始字节码-->
<execution>
<phase>test</phase>
<id>default-restore-instrumented-classes</id>
<goals>
<goal>restore-instrumented-classes</goal>
</goals>
</execution>
<!--report:在 test 阶段执行,生成覆盖率报告。-->
<execution>
<phase>test</phase>
<id>jacoco-report</id>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
再贴一下通过这种整合方式本地跑单测的效果:
本文由mdnice多平台发布
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。