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

u4dcyp6a  于 2023-02-02  发布在  Java
关注(0)|答案(5)|浏览(312)

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

[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.

蚂蚁报告目标如下所示:

<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生成的,那为什么会出现这样的错误,你知道吗?

zynd9foi

zynd9foi1#

您收到了与classID相关的错误。JaCoCo docs-site. http://www.eclemma.org/jacoco/trunk/doc/classids.html中详细描述了这个概念。这是在同一JVM中支持多个版本的类(例如一个appserver)的关键步骤。
复制其中的一部分以提高可见度。

    • 什么是类ID以及如何创建它们?**

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

    • 什么会导致不同的类ID?**

类ID只有在完全相同的类文件中才是相同的(一个字节接一个字节)。您可能会得到不同的类文件,原因有两个。首先,如果您使用不同的工具链,编译Java源文件将得到不同的类文件:

  • 不同的编译器供应商(例如Eclipse与Oracle JDK)
  • 不同的编译器版本
  • 不同的编译器设置(例如调试与非调试)

同样,后处理类文件(obfuscation、AspectJ等)通常也会改变类文件。如果您只是简单地使用相同的类文件进行运行时和分析,JaCoCo将工作得很好。因此创建这些类文件的工具链并不重要。
即使文件系统上的类文件是相同的,JaCoCo运行时代理看到的类也可能是不同的。这通常发生在JaCoCo代理或特殊类加载器预处理类文件之前配置了另一个Java代理的情况下。

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

同一页还介绍了可能的解决方案。

    • 有哪些变通方法可以处理运行时修改的类?**

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

  • 如果你使用另一个Java代理,确保JaCoCo代理在命令行中被指定,这样JaCoCo代理就可以看到原始的类文件。
  • 指定JaCoCo代理的classdumpdir选项并在生成报告时使用转储的类。请注意,只有加载的类才会被转储,即根本未执行的类不会在报告中显示为未涵盖。
  • 在运行测试之前使用离线插桩。这样类在任何运行时修改发生之前就被JaCoCo插桩了。注意在这种情况下,报告必须用原始类生成,而不是用插桩的类。
    • 编辑日期:2017年2月22日**
    • 如何使用离线指令插入:**使用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"命令生成报告。

x759pob2

x759pob22#

JaCoCo需要与执行时使用的报告生成完全相同的类文件。由于不同的编译器和/或修改类的其他工具,类可能会有所不同。

gjmwrych

gjmwrych3#

我注意到,如果您要报告代码覆盖率的类在JUnit测试中使用PowerMockito时被Mockito抑制了静态初始化,就会发生这种情况。

@SuppressStaticInitializationFor(
        {"com.yourpkg.A",
        "com.yourpkg.B"})
public class Test {
     @Test
     public void Test() { }
}

测试时的错误如下所示:

Classes in bundle 'yourProject' do not match with execution data. 
For report generation the same class files must be used as at runtime. 
Execution data for class com/yourpkg/A does not match. 
Execution data for class com/yourpkg/B does not match.
amrnrhlw

amrnrhlw4#

各种答案与深刻的见解,但我仍然会分享什么为我工作后,2天的摆弄设置。

  • 我正在为Git使用Azure Devops。

因此,根据代码设置指南,您需要在构建任务之后运行分析任务,而这正是我完全忽略的步骤。
下面是我之前的任务序列

- task: Gradle@2
    inputs:
      gradleWrapperFile: 'gradlew'
      tasks: '--build-cache jacocoTestReport SonarQube'
      options: '-PversionName=$(Build.BuildNumber) -PversionCode=$(Build.BuildId) -Porg.gradle.parallel=true'
      publishJUnitResults: true
      testResultsFiles: '**/TEST-*.xml'
      javaHomeOption: 'JDKVersion'
      jdkVersionOption: '1.11'
      gradleOptions: '-Xmx4096m'
      sonarQubeRunAnalysis: false
    continueOnError: true
    displayName: "Run Quality Scan and upload"
    env:
      SONAR_LOGIN: $(sonar-login)
      BRANCH_NAME: $(Build.SourceBranchName)
      
  - task: Gradle@2
    condition: in(variables['Build.SourceBranchName'], 'develop')
    inputs:
      gradleWrapperFile: 'gradlew'
      tasks: '--build-cache build publishAllPublicationsToMyDigitalRepository'
      options: '-PversionName=$(Build.BuildNumber) -PversionCode=$(Build.BuildId) -PsecureSign'
      publishJUnitResults: true
      testResultsFiles: '**/TEST-*.xml'
      javaHomeOption: 'JDKVersion'
      jdkVersionOption: '1.11'
      gradleOptions: '-Xmx4096m'
      sonarQubeRunAnalysis: false
    env:
      SDK_NAME: 'iphoneos'
    continueOnError: false
    displayName: "Build & publish library"

这是重新定位后的情况

- task: Gradle@2
    condition: in(variables['Build.SourceBranchName'], 'develop')
    inputs:
      gradleWrapperFile: 'gradlew'
      tasks: '--build-cache build publishAllPublicationsToMyDigitalRepository'
      options: '-PversionName=$(Build.BuildNumber) -PversionCode=$(Build.BuildId) -PsecureSign'
      publishJUnitResults: true
      testResultsFiles: '**/TEST-*.xml'
      javaHomeOption: 'JDKVersion'
      jdkVersionOption: '1.11'
      gradleOptions: '-Xmx4096m'
      sonarQubeRunAnalysis: false
    env:
      SDK_NAME: 'iphoneos'
    continueOnError: false
    displayName: "Build & publish library"

  - task: Gradle@2
    inputs:
      gradleWrapperFile: 'gradlew'
      tasks: '--build-cache jacocoTestReport SonarQube'
      options: '-PversionName=$(Build.BuildNumber) -PversionCode=$(Build.BuildId) -Porg.gradle.parallel=true'
      publishJUnitResults: true
      testResultsFiles: '**/TEST-*.xml'
      javaHomeOption: 'JDKVersion'
      jdkVersionOption: '1.11'
      gradleOptions: '-Xmx4096m'
      sonarQubeRunAnalysis: false
    continueOnError: true
    displayName: "Run Quality Scan and upload"
    env:
      SONAR_LOGIN: $(sonar-login)
      BRANCH_NAME: $(Build.SourceBranchName)

所以本质上我学到了一些东西,脱离了上下文,但最重要的是。

  • 任务的正确顺序
  • 声纳要正确地找到这些文件,就必须有可用的版本。
o2rvlv0m

o2rvlv0m5#

添加离线检测后,覆盖率正常,但我开始收到class is already instrumented消息。在build.gradle中添加此消息会有所帮助。

test {
    jacoco {
        excludes = ['{package_path}']     //package_path like '*com/**'
    }
}

相关问题