单元测试之关于JaCoCo和PowerMock冲突导致类覆盖率为0的问题

在使用Mockito和PowerMock写单测的时候发现,如果使用了PowerMock的@PrepareForTest注解,JaCoCo在统计代码覆盖率的时候就会忽略注解@PrepareForTest({})里面的类,导致覆盖率统计不准确

对于一些工具类,我们可以先使用@PrepareForTest完成单测的开发,再单独对工具类写单测就行了,只要@PrepareForTest里面的类不是自己的项目代码就可以了(比方说第三方jar包提供的), 但是在遇到系统类的时候就不行了。因为PowerMock 在mock系统类的返回结果时,就需要在@PrepareForTest{()}里写上调用系统类的Class,这样对应的业务类就无法被统计到

这是由于@PrepareForTest和java 覆盖率工具(Java Code Coverage – JaCoCo)冲突导致的

这是官方的解释:
Code coverage with JaCoCo

The simplest way to use JaCoCo it is — on-the-fly instrumentation with using JaCoCo Java Agent. In this case a class in modified when it is being loaded. You can just run you application with JaCoCo agent and a code coverage is calculated. This way is used by Eclemma and Intellij Idea. But there is a big issue. PowerMock instruments classes also. Javassist is used to modify classes. The main issue is that Javassist reads classes from disk and all JaCoCo changes are disappeared. As result zero code coverage for classes witch are loaded by PowerMock class loader.

JaCoCo和PowerMock都是通过在加载类的时候修改字节码文件来实现统计覆盖率和mock静态类的功能。JaCoCo在加载class的时候会把统计代码插入到class中,而PowerMock当使用了@PrepareForTest注解,在加载相关类的时候会从class文件重新读取字节码信息,导致JaCoCo的修改都没有了,所以就没办法统计到了

为了解决上述问题,Github上也给出了一个解决方案,那就是使用JaCoCo的offline模式

<!--- 定义 jacoco 版本 -->
<properties>
  <jacoco.version>0.8.5</jacoco.version>
</properties>
<!--- 定义 jacoco 执行 offline 模式 goals -->
<build>
  <plugins>
    <!-- 注意不是在pluginManagement, pluginManagement中只是声明 -->
    <plugin>
      <groupId>org.jacoco</groupId>
      <artifactId>jacoco-maven-plugin</artifactId>
      <version>${jacoco.version}</version>
      <executions>
        <execution>
          <id>default-instrument</id>
          <goals>
            <goal>instrument</goal>
          </goals>
        </execution>
        <execution>
          <id>default-restore-instrumented-classes</id>
          <goals>
            <goal>restore-instrumented-classes</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <!--离线模式必需指定, 否则到模块根目录而不是target目录了-->
        <configuration>
            <systemPropertyVariables>
                <jacoco-agent.destfile>target/jacoco.exec</jacoco-agent.destfile>
            </systemPropertyVariables>
        </configuration>
    </plugin>
  </plugins>
</build>
<!--- 定义 jacoco 依赖  注意不是在dependencyManagement, dependencyManagement中只是声明-->
<dependencies>
  <dependency>
    <groupId>org.jacoco</groupId>
    <artifactId>org.jacoco.agent</artifactId>
    <version>${jacoco.version}</version>
    <classifier>runtime</classifier>
  </dependency>
</dependencies>

这样哪怕使用了@PrepareForTest注解,也不会影响到JaCoCo的代码覆盖率统计了,不过需要注意的是

Offline模式单元测试不能跨模块, 不能源码在A模块单测写在B模块

比方说:项目分成了四个模块,service,utils,dao,controller,你在写service单测的时候哪怕调用了utils的代码,但是在实际统计覆盖率的时候是没办法统计到的,需要单独针对每一个模块写单测

你可能感兴趣的:(单元测试,PowerMock)