JaCoCo无法与Robolectric测试一起使用。

26

我希望在我的Android项目中对JUnit测试生成代码覆盖率报告,因此我添加了JaCoCo Gradle插件。这是我的项目级别的build.gradle文件:

apply plugin: 'jacoco'

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.0.0-beta6'
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}

allprojects {
    repositories {
        jcenter()
        maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

subprojects { prj ->
    apply plugin: 'jacoco'

    jacoco {
        toolVersion '0.7.6.201602180812'
    }

    task jacocoReport(type: JacocoReport, dependsOn: 'testDebugUnitTest') {
        group = 'Reporting'
        description = 'Generate Jacoco coverage reports after running tests.'

        reports {
            xml {
                enabled = true
                destination "${prj.buildDir}/reports/jacoco/jacoco.xml"
            }
            html {
                enabled = true
                destination "${prj.buildDir}/reports/jacoco"
            }
        }

        classDirectories = fileTree(
                dir: 'build/intermediates/classes/debug',
                excludes: [
                        '**/R*.class',
                        '**/BuildConfig*',
                        '**/*$$*'
                ]
        )

        sourceDirectories = files('src/main/java')
        executionData = files('build/jacoco/testDebugUnitTest.exec')

        doFirst {
            files('build/intermediates/classes/debug').getFiles().each { file ->
                if (file.name.contains('$$')) {
                    file.renameTo(file.path.replace('$$', '$'))
                }
            }
        }
    }
}

jacoco {
    toolVersion '0.7.6.201602180812'
}

task jacocoFullReport(type: JacocoReport, group: 'Coverage reports') {
    group = 'Reporting'
    description = 'Generates an aggregate report from all subprojects'

    //noinspection GrUnresolvedAccess
    dependsOn(subprojects.jacocoReport)

    additionalSourceDirs = project.files(subprojects.jacocoReport.sourceDirectories)
    sourceDirectories = project.files(subprojects.jacocoReport.sourceDirectories)
    classDirectories = project.files(subprojects.jacocoReport.classDirectories)
    executionData = project.files(subprojects.jacocoReport.executionData)

    reports {
        xml {
            enabled = true
            destination "${buildDir}/reports/jacoco/full/jacoco.xml"
        }
        html {
            enabled = true
            destination "${buildDir}/reports/jacoco/full"
        }
    }

    doFirst {
        //noinspection GroovyAssignabilityCheck
        executionData = files(executionData.findAll { it.exists() })
    }
}

通过运行./gradlew jacocoFullReport可以很好地工作。但是不幸的是,使用RobolectricTestRunner运行的测试未报告覆盖范围(明显在测试中调用的指令未被报告为已覆盖)。没有@RunWith注释或使用MockitoJUnitTestRunner运行的测试报告覆盖率正常。

希望能有帮助解决这个问题。

更新1:我注意到应该使用RobolectricGradleTestRunner。但这并没有帮助。


我想说这肯定是有可能的,但我不知道如何解决。我曾经在一份旧工作中成功地让Robolectric能够进行代码覆盖率测试,但那已经过去一年了,我现在无法再访问那段代码了。很抱歉我不能提供更多帮助! - Joseph Roque
在Gradle的后续版本中,testCoverageEnabled使用Jacoco,您无需应用插件。尝试不使用插件。 - Nikola Despotoski
@NikolaDespotoski 我相信将testCoverageEnabled设置为true只适用于需要连接设备的Android仪器化测试。而我使用Robolectric正是为了避免这种情况发生。 - Longi
6个回答

24

这是一个已知问题,有可能的解决方法是 - https://github.com/jacoco/jacoco/pull/288

或将 jacoco 版本降级至 0.7.1.201405082137

更新

不再需要使用解决方法。您必须使用 gradle 版本 2.13jacoco 版本 0.7.6.201602180812

更新根目录下的 build.gradle 文件:

buildscript {
    dependencies {
        classpath 'org.jacoco:org.jacoco.core:0.7.6.201602180812'
    }
}

task wrapper( type: Wrapper ) {
  gradleVersion = '2.13'
}

运行 ./gradlew wrapper

更新项目中的 build.gradle

apply plugin: 'jacoco'

android {
  testOptions {
    unitTests.all {
      jacoco {
        includeNoLocationClasses = true
      }
    }
  }
}

1
不幸的是,这个解决方法似乎对我无效。 - Longi
在这种情况下,如果对您来说不是主要问题,那么请将jacoco降级到0.7.1.201405082137的较低版本。如果您使用的是Jenkins最新的jacoco插件,则仍可能存在问题。此外,请将其版本降级至1.8左右。 - Eugen Martynov
降级jacoco就解决了问题。我使用travis-ci,它没有任何问题。 - Longi
我不确定,但如果你将其删除,那么你将使用默认的Jacoco插件,我不确定它是否是最新的。你试过这个吗? - Eugen Martynov
我已经在Jacoco上实现了:toolVersion = "0.7.7.201606060606",对我来说改变的只是添加了includeNoLocationClasses = true。 - Ryan Newsom
显示剩余3条评论

7
接受的答案有点过时。这是我们刚刚实施的类似修复方法。在模块(即应用)build.gradle中添加以下内容:
apply plugin: 'jacoco'

tasks.withType(Test) {
    jacoco.includeNoLocationClasses = true
}

这需要 JaCoCo 7.6+,但是您可能已经在使用它了。

Studio 注意事项:

  1. 这仅修复 CLI。如果您在 Studio 中使用 JaCoCo 运行覆盖率,Robolectric 覆盖率仍然不会报告。默认的 IntelliJ 覆盖率运行程序似乎工作正常。
  2. 测试在 Studio 中间歇性崩溃,除非我将 -noverify 添加到 Android JUnit -> VM 选项中。

1
这似乎是迄今为止最准确的答案。 - Flávio Faria

4

Eugen Martynov建议了同样的事情。但是对我没有用。 - Longi

3

这是一个老问题,但对于那些仍然面临此问题的人来说,值得一提的是,如果您正在设置 JaCoCo + Robolectric + Espresso - 您确实将使用 includeNoLocationClasses,但仍然会在 Java9+ 中看到此错误并可能最终到达此处。请在您的模块 build.gradle 文件中添加下面的代码片段。

tasks.withType(Test) {
  jacoco.includeNoLocationClasses = true
  jacoco.excludes = ['jdk.internal.*']
}

非常感谢您提到jacoco.excludes语句。如果没有它,我的项目中的单元测试就会崩溃。 - Nantoka

3

我遇到了同样的问题。我更改了jacoco插件版本并添加了includenolocationclasses属性。这是可用的jacoco.gradle文件(我使用gradle wrapper 2.14.1):

apply plugin: 'jacoco'

jacoco {
    toolVersion = "0.7.6.201602180812"
}

android {
    testOptions {
        unitTests.all {
            jacoco {
                includeNoLocationClasses = true
            }
        }
    }
}

project.afterEvaluate {
    // Grab all build types and product flavors
    def buildTypes = android.buildTypes.collect { type -> type.name }
    def productFlavors = android.productFlavors.collect { flavor -> flavor.name }
    println(buildTypes)
    println(productFlavors)
    // When no product flavors defined, use empty
    if (!productFlavors) productFlavors.add('')

    productFlavors.each { productFlavorName ->
        buildTypes.each { buildTypeName ->
            def sourceName, sourcePath
            if (!productFlavorName) {
                sourceName = sourcePath = "${buildTypeName}"
            } else {
                sourceName = "${productFlavorName}${buildTypeName.capitalize()}"
                sourcePath = "${productFlavorName}/${buildTypeName}"
            }
            def testTaskName = "test${sourceName.capitalize()}UnitTest"
            println("SourceName:${sourceName}")
            println("SourcePath:${sourcePath}")
            println("testTaskName:${testTaskName}")
            // Create coverage task of form 'testFlavorTypeCoverage' depending on 'testFlavorTypeUnitTest'
            task "${testTaskName}Coverage" (type:JacocoReport, dependsOn: "$testTaskName") {
                group = "Reporting"
                description = "Generate Jacoco coverage reports on the ${sourceName.capitalize()} build."

                classDirectories = fileTree(
                        dir: "${project.buildDir}/intermediates/classes/${sourcePath}",
                        excludes: ['**/R.class',
                                   '**/R$*.class',
                                   '**/*$ViewInjector*.*',
                                   '**/*$ViewBinder*.*',
                                   '**/BuildConfig.*',
                                   '**/Manifest*.*']
                )

                def coverageSourceDirs = [
                        "src/main/java",
                        "src/$productFlavorName/java",
                        "src/$buildTypeName/java"
                ]
                additionalSourceDirs = files(coverageSourceDirs)
                sourceDirectories = files(coverageSourceDirs)
                executionData = files("${project.buildDir}/jacoco/${testTaskName}.exec")
                println("${project.buildDir}/jacoco/${testTaskName}.exec")
                reports {
                    xml.enabled = true
                    html.enabled = true
                }
            }
        }
    }
}

-1

在Android Studio中将覆盖率运行器更改为Jacoco 1- 选择应用程序(项目的根目录) 2- 点击菜单(运行 -> 编辑配置 -> 代码覆盖率 -> 选择JaCoCo)。

the screenshot is below


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接