jacoco 代码覆盖率报告生成器显示错误:“包‘代码覆盖率报告’中的类与执行数据不匹配"

jacoco 代码覆盖率报告生成器显示错误:“包‘代码覆盖率报告’中的类与执行数据不匹配

问题描述:

我正在使用 jacoco:report 标签生成 jacoco 报告.我收到如下错误:

I am generating jacoco report by using jacoco:report tag. I am getting errors like :

[jacoco:report] Classes in bundle 'Code Coverage Report' do no match with execution data. For report generation the same class files must be used as at runtime.
[jacoco:report] Execution data for class xxxxx does not match.
[jacoco:report] Execution data for class yyyyy does not match.

蚂蚁报告目标看起来像:

The ant report target looks like :

<target name="report">
                <jacoco:report>
                        <executiondata>
                                <file file="${jacocoexec.dir}/${jacocoexec.filename}"/>
                        </executiondata>
                        <!-- the class files and optional source files ... -->
                        <structure name="Code Coverage Report">
                                <classfiles>
                                        <fileset file="./jar/abc.jar"/>
                                </classfiles>
                                <sourcefiles>
                                      <fileset dir="./code/src"/>
                                </sourcefiles>
                        </structure>
                        <!-- to produce reports in different formats. -->
                        <html destdir="${jacoco.report.dir}"/>
                </jacoco:report>
        </target>

这样生成的 abc.jar 仅使用 ./code/src.那为什么会出现这样的错误.有什么想法吗?

The abc.jar so generated is by using ./code/src only. Then why is it giving such errors. Any idea?

您收到与 classID 相关的错误.这是在 JaCoCo docs-site 上详细描述的概念.http://www.eclemma.org/jacoco/trunk/doc/classids.html.这是在同一 JVM 中支持多个版本的类(例如应用服务器)的关键步骤.

You are getting the error related to classID. This is a concept described in detail at JaCoCo docs-site. http://www.eclemma.org/jacoco/trunk/doc/classids.html. This is a key step for supporting multiple versions of class (an appserver for example) in same JVM.

为了可见性,这里复制了其中的一部分.

Copying part some part of it here for visibility.

什么是类 ID,它们是如何创建的?

类 ID 是 64 位整数值,例如 0x638e104737889183十六进制表示法.他们的计算被认为是一个实现细节JaCoCo 的.当前 id 是使用原始数据的 CRC64 校验和创建的类文件.

Class ids are 64-bit integer values, for example 0x638e104737889183 in hex notation. Their calculation is considered an implementation detail of JaCoCo. Currently ids are created with a CRC64 checksum of the raw class file.

什么会导致不同的类 ID?

类 id 仅对于完全相同的类文件是相同的(逐字节).有几个原因会让你得到不同的类文件.首先编译Java源文件将导致如果您使用不同的工具链,则在不同的类文件中:

Class ids are identical for the exact same class file only (byte-by-byte). There is a couple of reasons why you might get different class files. First compiling Java source files will result in different class files if you use a different tool chain:

  • 不同的编译器供应商(例如 Eclipse 与 Oracle JDK)

    • Different compiler vendor (e.g. Eclipse vs. Oracle JDK)

      不同的编译器版本

      不同的编译器设置(例如调试与非调试)

      Different compiler settings (e.g. debug vs. non-debug)

      此外,后处理类文件(混淆、AspectJ 等)通常会更改类文件.如果您只是将相同的类文件用于运行时和分析,那么 JaCoCo 将运行良好.所以创建这些类文件的工具链并不重要.

      Also post-processing class files (obfuscation, AspectJ, etc.) will typically change the class files. JaCoCo will work well if you simply use the same class files for runtime as well as for analysis. So the tool chain to create these class files does not matter.

      即使文件系统上的类文件相同,JaCoCo 运行时代理看到的类也可能不同.这通常发生在在 JaCoCo 代理或特殊类加载器预处理类文件之前配置另一个 Java 代理时.典型的候选人是:

      Even if the class files on the file system are the same there is possible that classes seen by the JaCoCo runtime agent are different anyways. This typically happens when another Java agent is configured before the JaCoCo agent or special class loaders pre-process the class files. Typical candidates are:

      • 模拟框架
      • 应用服务器
      • 持久性框架

      同一页面涵盖了可能的解决方案.

      The same page covers possible solutions.

      处理运行时修改的类有哪些解决方法?

      如果类在您的设置中在运行时被修改,有一些变通方法可以让 JaCoCo 正常工作:

      If classes get modified at runtime in your setup there are some workarounds to make JaCoCo work anyways:

      • 如果您使用其他 Java 代理,请确保首先在命令行中指定 JaCoCo 代理.这样 JaCoCo 代理应该会看到原始类文件.
      • 指定 JaCoCo 代理的 classdumpdir 选项并在生成报告时使用转储的类.请注意,只会转储已加载的类,即根本未执行的类将不会显示在您的报告中,因为未涵盖.
      • 在运行测试之前使用离线检测.通过这种方式,JaCoCo 可以在任何运行时修改发生之前检测类.请注意,在这种情况下,报告必须使用原始类生成,而不是使用检测类生成.

      于 22-02-2017 编辑

      如何使用离线检测:使用以下由 Daniel Atallah 提供的任务.

      How to use Offline Instrumentation: Use below task provided by Daniel Atallah.

      //Additional SourceSets can be added to the jacocoOfflineSourceSets as needed by 
      project.ext.jacocoOfflineSourceSets = [ 'main' ]
      task doJacocoOfflineInstrumentation(dependsOn: [ classes, project.configurations.jacocoAnt ]) {
          inputs.files classes.outputs.files
          File outputDir = new File(project.buildDir, 'instrumentedClasses')
          outputs.dir outputDir
          doFirst {
              project.delete(outputDir)
              ant.taskdef(
                  resource: 'org/jacoco/ant/antlib.xml',
                  classpath: project.configurations.jacocoAnt.asPath,
                  uri: 'jacoco'
              )
              def instrumented = false
              jacocoOfflineSourceSets.each { sourceSetName ->
                  if (file(sourceSets[sourceSetName].output.classesDir).exists()) {
                      def instrumentedClassedDir = "${outputDir}/${sourceSetName}"
                      ant.'jacoco:instrument'(destdir: instrumentedClassedDir) {
                          fileset(dir: sourceSets[sourceSetName].output.classesDir, includes: '**/*.class')
                      }
                      //Replace the classes dir in the test classpath with the instrumented one
                      sourceSets.test.runtimeClasspath -= files(sourceSets[sourceSetName].output.classesDir)
                      sourceSets.test.runtimeClasspath += files(instrumentedClassedDir)
                      instrumented = true
                  }
              }
              if (instrumented) {
                  //Disable class verification based on https://github.com/jayway/powermock/issues/375
                  test.jvmArgs += '-noverify'
              }
          }
      }
      test.dependsOn doJacocoOfflineInstrumentation
      

      现在使用 "gradlew test jacocoTestReport" 命令生成报告.

      Now generate report using "gradlew test jacocoTestReport" command.