有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java Kotlin JaCoCo IllegalClassFormatException。请提供原始的非仪器类

我正在尝试获取Android应用程序模块的测试覆盖率报告,并执行testVariantBuildTypeUnitTest任务

尽管所有测试都通过了,测试classNameTest。xml文件包含以下错误消息。此错误仅适用于包含来自Kotlin的Java调用的方法。这个问题有什么解决办法吗

**Caused by: java.lang.IllegalStateException: Cannot process instrumented class... Please supply original non-instrumented classes.
at org.jacoco.agent.rt.internal_f3994fa.core.internal.instr.InstrSupport.assertNotInstrumented(InstrSupport.java:238)
at org.jacoco.agent.rt.internal_f3994fa.core.internal.instr.ClassInstrumenter.visitField(ClassInstrumenter.java:56)
at org.jacoco.agent.rt.internal_f3994fa.asm.ClassVisitor.visitField(ClassVisitor.java:339)
at org.jacoco.agent.rt.internal_f3994fa.asm.ClassReader.readField(ClassReader.java:1111)
at org.jacoco.agent.rt.internal_f3994fa.asm.ClassReader.accept(ClassReader.java:713)
at org.jacoco.agent.rt.internal_f3994fa.asm.ClassReader.accept(ClassReader.java:401)
at org.jacoco.agent.rt.internal_f3994fa.core.instr.Instrumenter.instrument(Instrumenter.java:90)
at org.jacoco.agent.rt.internal_f3994fa.core.instr.Instrumenter.instrument(Instrumenter.java:108)

我们有多模块安卓项目,因此我们使用此自定义Jacoco任务获取覆盖率报告:

project.afterEvaluate {

    (安卓.hasProperty('applicationVariants')
            ? 安卓.'applicationVariants'
            : 安卓.'libraryVariants')
            .all { variant ->
                def variantName = variant.name
                def unitTestTask = "test${variantName.capitalize()}UnitTest"

                def jacocoReportName = unitTestTask + project.name

                tasks.create(name: jacocoReportName, type: JacocoReport, dependsOn: [
                        "$unitTestTask"
                ]) {
                    group = "Reporting"
                    description = "Generate Jacoco coverage reports for the ${variantName.capitalize()} build"

                    reports {
                        html.enabled = true
                        xml.enabled = true
                    }

                    def fileFilter = [
                            // data binding
                            '安卓/databinding/**/*.class',
                            '**/安卓/databinding/*Binding.class',
                            '**/安卓/databinding/*',
                            '**/安卓x/databinding/*',
                            '**/databinding',
                            '**/BR.*',
                            // 安卓
                            '**/R.class',
                            '**/R$*.class',
                            '**/BuildConfig.*',
                            '**/Manifest*.*',
                            '**/*Test*.*'
                    ]

                    def javaClasses = fileTree(dir: variant.javaCompileProvider.get().destinationDir,
                            excludes: fileFilter)
                    def kotlinClasses = fileTree(dir: "${buildDir}/tmp/kotlin-classes/${variantName}",
                            excludes: fileFilter)

                    classDirectories.setFrom(files([
                            javaClasses,
                            kotlinClasses
                    ]))

                    def variantSourceSets = variant.sourceSets.java.srcDirs.collect { it.path }.flatten()
                    sourceDirectories.setFrom(project.files(variantSourceSets))

                    if (isAndroidLibrary(project)) {
                        executionData(files([
                                "${projectDir}/jacoco.exec"
                        ]))
                    }else{
                        executionData(files([
                                "$project.buildDir/jacoco/${project.name}.exec"
                        ]))
                    }

                }

            }
}

问候,


共 (1) 个答案

  1. # 1 楼答案

    解决方案

    问题很可能存在于您的模块级构建中。gradle文件。您需要删除testCoverageEnabled或将其设置为false

    android {
     ...
      buildTypes {
         release {
            minifyEnabled true
         }
         debug {
            // testCoverageEnabled true 
            minifyEnabled false
         }
      }
    
    
    解释和参考资料
    • 首先,我与JaCoCo多年来一直在努力解决android上的单元测试覆盖问题,并且在AGP(android Gradle插件)团队增强AGP(例如Similar issue)时多次遇到这些问题,因此该解决方案可能会在未来的AGP版本中发生变化

    • 在AGP4.1的release中,AGP团队改变了插件处理单元测试的方式。我向AGP团队提出了一个enter link description here问题,他们解释说插件(AGP4.1+)使用自己的工具形式来获得覆盖,这无疑与JaCoCo不兼容。正是在这个问题上,我强调了解决方案

    注1:此问题在DSL文档中有not documented

    注2:还应注意的是,在我编写本AGP7时,同样的问题也出现在开发中

    注意事项

    此解决方案将导致生成插装测试覆盖率时出现问题,因为AGP生成插装测试覆盖率(以.ec文件的形式),这意味着要获得插装测试覆盖率,您必须具有testCoverageEnabled true。 作为解决办法,您可以:

    • 将您的单元测试放在发布版本变体中
    • 将插入指令的测试放在调试生成变量中。 仍然可以将覆盖范围合并到一个报告中:例如:
    task jacocoCombinedUnitTestAndroidTestReport(type: JacocoReport) {
            group = "Reporting"
    
            reports {
                xml.enabled = true
                html.enabled = true
            }
    
            def mainSrc = "$project.projectDir/src/main/java"
            def releaseKotlinClasses = fileTree(dir: "${buildDir}/tmp/kotlin-classes/release", excludes:["com/example/ui**"])
            def debugKotlinClasses = fileTree(dir: "${buildDir}/tmp/kotlin-classes/debug/com/example/ui/")
            def releaseJavaClasses = fileTree(dir: "${buildDir}/intermediates/javac/release", excludes: ["com/example/ui**"])
            def debugJavaClasses = fileTree(dir: "${buildDir}/intermediates/javac/debug/com/example/ui/" )
    
            def mainClasses = releaseKotlinClasses + debugKotlinClasses
            def javaClasses = releaseJavaClasses + debugJavaClasses
    
            sourceDirectories.from = files([mainSrc])
            classDirectories.from = files([mainClasses, javaClasses])
            executionData.from = fileTree(dir: buildDir, include: ["jacoco/testReleaseUnitTest.exec",
                                                                        "outputs/code_coverage/debugAndroidTest/connected/**.ec"])
    }
    
    • 这假设您在UI目录中有任何UI代码
    • 包括除ui目录之外的所有发布类
    • 您只包括ui调试类

    请注意,这是唯一有效的,当这个答案是书面的,希望谷歌将解决这个问题在未来