Jacoco is reporting 0 coverage of Kotlin classes by unit tests, in an Android project

Viewed 3516

I'm using Android Gradle Plugin 3.0.0.

I'm migrating an Android app from java to kotlin. My app has classes in Java and Kotlin, and tests are in Java.

I run ./gradlew clean jacocoTestReport.

This runs both unit tests (src/test) and instrumentation tests (src/androidTest).

The report produced by jacoco in app/build/reports/jacoco/jacocoTestReport/html/index.html doesn't show coverage for Kotlin classes which are indeed covered by unit tests.

The report does show coverage correctly from instrumentation tests.

Note: I came across these other questions, which aren't exactly the same issue:

Relevant portions of my app module's build.gradle:

apply plugin: 'jacoco'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
...
android {
    defaultConfig {
        sourceSets {
            main.java.srcDirs += "$projectDir/src/main/kotlin"
        }
    }

    testOptions {
        unitTests {
            all {
                jvmArgs '-noverify', '-ea'
            }
            includeAndroidResources = true
        }
    }
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    ....
}

jacoco {
    toolVersion '0.7.9'
}

task jacocoTestReport(type: JacocoReport, dependsOn: ["testDebugUnitTest", "createDebugCoverageReport"]) {
    reports {
        html.enabled = true
    }
    classDirectories = fileTree(
            dir: "${buildDir}",
            includes: ["tmp/kotlin-classes/debug/ca/rmen/android/poetassistant/**/*.class",
                       "intermediates/classes/debug/ca/rmen/android/poetassistant/**/*.class"],
            excludes: ["**/R.class", "**/R*.class", "**/Manifest.class", "**/Manifest*.class", "**/BuildConfig.class",
                       // ignore databinding generated code:
                       "**/ca/rmen/android/poetassistant/databinding/*.class",
                       ... other excludes ...
                       "**/ca/rmen/android/poetassistant/BR.class",
                       "**/com/android/**/*.class"])
    sourceDirectories = files("${project.projectDir}/src/main/java", "${project.projectDir}/src/main/kotlin")
    executionData = fileTree(
            dir: "${buildDir}",
            includes: [
                    "jacoco/testDebugUnitTest.exec",
                    "outputs/code-coverage/connected/*coverage.ec"
            ])
}
2 Answers

I had to add includeNoLocationClasses = true to my gradle file as follows, to make the jacoco report reflect the coverage of Kotlin classes by unit tests:

android {
    testOptions {
        unitTests {
            all {
                jvmArgs '-noverify', '-ea'
                jacoco {
                    includeNoLocationClasses = true
                }
            }
            includeAndroidResources = true
        }
    }
}

Note: this solution works for running tests from the command line, but I still get 0% coverage when running with coverage from inside Android Studio.

Add below line in jacoco.gradle file for getting the kotlin classes

            def kotlinDebugTree = fileTree(dir: "${buildDir}/tmp/kotlin-classes/debug", excludes: fileFilter)

Ex.

def mainSrc = "${project.projectDir}/src/main/java"
            def debugTree = fileTree(dir: "${buildDir}/intermediates/classes/debug", excludes: fileFilter)
            def kotlinDebugTree = fileTree(dir: "${buildDir}/tmp/kotlin-classes/debug", excludes: fileFilter)
            sourceDirectories.from = files([mainSrc])
            classDirectories.from = files([debugTree], [kotlinDebugTree])
            executionData.from = fileTree(dir: buildDir, includes: [
                    "jacoco/test${name}UnitTest.exec",
                    'outputs/code-coverage/connected/*coverage.ec'
            ])

After the adding this line execute the jacoco command again and it will give the coverage of unit test cases also.

Related