本文对应源码:欢迎关注我的公众号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

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多平台发布


nrsc
1 声望0 粉丝