简体   繁体   English

JaCoCo 不适用于 Robolectric 测试

[英]JaCoCo doesn't work with Robolectric tests

I wanted to generate code coverage reports on my JUnit tests in my android project so I added the JaCoCo gradle plugin.我想为我的 android 项目中的 JUnit 测试生成代码覆盖率报告,因此我添加了 JaCoCo gradle 插件。 This is my project level build.gradle file:这是我的项目级别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() })
    }
}

It works great by running ./gradlew jacocoFullReport .通过运行./gradlew jacocoFullReport But unfortunately coverage is not reported for the tests that are run with the RobolectricTestRunner (instructions that are obviously called in the tests are not reported as covered).但不幸的是,没有报告使用RobolectricTestRunner运行的测试的覆盖率(在测试中明显调用的指令未报告为已覆盖)。 Tests with no @RunWith annotation or run with MockitoJUnitTestRunner report coverage just fine.没有@RunWith注释的测试或使用MockitoJUnitTestRunner报告覆盖率运行都很好。

Any help would be appreciated to fix this problem.解决此问题的任何帮助将不胜感激。

Update 1: I noticed that I should be using the RobolectricGradleTestRunner .更新 1:我注意到我应该使用RobolectricGradleTestRunner But it didn't help.但这没有帮助。

It is known issue with the possible workaround - https://github.com/jacoco/jacoco/pull/288 已知问题可能的解决方法 - https://github.com/jacoco/jacoco/pull/288

Or downgrade jacoco version to 0.7.1.201405082137 或者将jacoco版本降级到0.7.1.201405082137

UPDATE UPDATE

The workaround is not needed anymore. 不再需要解决方法。 You must use gradle version 2.13 and jacoco version 0.7.6.201602180812 . 您必须使用gradle版本2.13jacoco版本0.7.6.201602180812

Update root build.gradle : 更新root build.gradle

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

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

Run ./gradlew wrapper 运行./gradlew wrapper

Update project build.gradle : 更新项目build.gradle

apply plugin: 'jacoco'

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

I was facing the same issue but now it is resolved for me by following this link, 我遇到了同样的问题,但现在通过以下链接解决了这个问题,

issue link: https://github.com/robolectric/robolectric/issues/2230 问题链接: https//github.com/robolectric/robolectric/issues/2230

Solution for this problem is mentioned here: 这里提到了解决这个问题的方法:

https://github.com/dampcake/Robolectric-JaCoCo-Sample/commit/f9884b96ba5e456cddb3d4d2df277065bb26f1d3 https://github.com/dampcake/Robolectric-JaCoCo-Sample/commit/f9884b96ba5e456cddb3d4d2df277065bb26f1d3

I had the same issue. 我遇到过同样的问题。 I changed the jacoco plugin version and added includenolocationclasses property. 我更改了jacoco插件版本并添加了includenolocationclasses属性。 Here is the working jacoco.gradle file (I am using gradle wrapper 2.14.1): 这是工作的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
                }
            }
        }
    }
}

The accepted answer is a bit dated. 接受的答案有点过时了。 Here is a similar fix we just implemented. 这是我们刚刚实施的类似修复程序。 In the module (ie app) build.gradle add: 在模块(即app)build.gradle中添加:

apply plugin: 'jacoco'

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

This does require JaCoCo 7.6+, but you are likely using it already. 这确实需要JaCoCo 7.6+,但您可能已经使用它了。

Notes for Studio: Studio的注释:

  1. This only fixes the CLI. 这只能修复CLI。 If you run coverage from Studio using JaCoCo, the Robolectric coverage is still not reported. 如果您使用JaCoCo从Studio运行覆盖,则仍未报告Robolectric覆盖。 The default IntelliJ Coverage Runner seems to work fine. 默认的IntelliJ Coverage Runner似乎运行正常。
  2. The test were crashing intermittently in Studio unless I added -noverify to the Android JUnit -> VM Options 测试在Studio中间歇性地崩溃,除非我将-noverify添加到Android JUnit - > VM Options

This is an old issue, but for those who are still facing, it is worth mentioning that if you are setting up JaCoCo + Robolectric + Espresso - you will indeed be using includeNoLocationClasses , but still will see this error with Java9+ and probably end up here.这是一个老问题,但对于那些仍然面临的人来说,值得一提的是,如果您正在设置 JaCoCo + Robolectric + Espresso - 您确实会使用includeNoLocationClasses ,但仍然会在 Java9+ 中看到此错误,并且可能会在这里结束. Add the below snippet to your module build.gradle file将以下代码片段添加到您的模块build.gradle文件中

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

change coverage runner to jacoco in android studio 1- select app(root of the project) 2 click on menu (run --> Edit configurations --> code coverage --> choose JaCoCo). 在android studio中将覆盖运行器更改为jacoco 1-选择应用程序(项目的根目录)2单击菜单(运行 - >编辑配置 - >代码覆盖 - >选择JaCoCo)。

屏幕截图如下

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM