Search code examples
androidcode-coveragerobolectricjacoco

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. This is my project level build.gradle file:

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. 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). Tests with no @RunWith annotation or run with MockitoJUnitTestRunner report coverage just fine.

Any help would be appreciated to fix this problem.

Update 1: I noticed that I should be using the RobolectricGradleTestRunner. But it didn't help.


Solution

  • It is known issue with the possible workaround - https://github.com/jacoco/jacoco/pull/288

    Or downgrade jacoco version to 0.7.1.201405082137

    UPDATE

    The workaround is not needed anymore. You must use gradle version 2.13 and jacoco version 0.7.6.201602180812.

    Update 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

    Update project build.gradle:

    apply plugin: 'jacoco'
    
    android {
      testOptions {
        unitTests.all {
          jacoco {
            includeNoLocationClasses = true
          }
        }
      }
    }