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

标签 java android unit-testing kotlin jacoco

我正在尝试获取我们的 Android 应用程序模块的测试覆盖率报告并执行 testVariantBuildTypeUnitTest 任务。
尽管所有测试都通过了,测试类名称Test.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)
我们有多模块 android 项目,因此我们使用这个自定义 Jacoco 任务来获取覆盖率报告:
project.afterEvaluate {

    (android.hasProperty('applicationVariants')
            ? android.'applicationVariants'
            : android.'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
                            'android/databinding/**/*.class',
                            '**/android/databinding/*Binding.class',
                            '**/android/databinding/*',
                            '**/androidx/databinding/*',
                            '**/databinding',
                            '**/BR.*',
                            // android
                            '**/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"
                        ]))
                    }

                }

            }
}

问候,

最佳答案

解决方案
问题很可能在于您的模块级别 build.gradle 文件。您需要 删除 testCoverageEnabled 或将其设置为假。

android {
 ...
  buildTypes {
     release {
        minifyEnabled true
     }
     debug {
        // testCoverageEnabled true 
        minifyEnabled false
     }
  }

解释和引用
  • 首先,我多年来一直在使用 JaCoCo 在 android 上进行单元测试覆盖,并且随着 AGP(Android Gradle 插件)团队增强 AGP(例如 Similar issue)多次遇到问题,因此该解决方案将来可能会发生变化AGP 的发布。
  • 快来release AGP 4.1 AGP 团队改变了插件处理单元测试的方式。我提出了 enter link description here AGP 团队的问题,他们解释说插件(AGP 4.1+)使用它自己的检测形式来获得与 JaCoCo 无疑不兼容的覆盖范围。正是在这个问题上,我向我强调了解决方案。

  • 注1:本期为 not documented 在 DSL 文档中!
    注意 2:还应注意,在我编写此 AGP 7 时,该 AGP 7 正在开发中,其中发生了同样的问题。
    注意事项
    此解决方案将导致为仪表测试生成覆盖率的问题,因为 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 调试类。

  • 请注意,这仅在撰写此答案时有效,希望 Google 将来会解决此问题。

    关于java - Kotlin JaCoCo - IllegalClassFormatException。请提供原始的非仪器类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67299155/

    相关文章:

    android - 如何知道一个应用程序是第一次下载还是多次从市场下载?

    javascript - 如何将自定义错误消息传递给 Jasmine 匹配器?

    java - PaintComponent 上的框架和形状的背景颜色在 Netbeans 上不起作用

    java - Hibernate 升级到 5.2.3 及更高版本的未知实体

    android - 如何扩大android图形布局屏幕尺寸?

    android - 您如何清除确定的进度条以供重用?

    java - 为什么Hashtable的equals方法要测试value是否为null的情况

    java - 我如何使用 JLabel?

    ios - 单元测试错误地从启动 View Controller 执行代码

    unit-testing - CI 服务器上应该运行什么样的测试?