2

I am in the process of modularising the app I am working on and my coverage has dropped over 20% since splitting my instrumentation tests into the app module.

The app is being split into app, core, custom, where core is an android library and the other 2 modules are apps. The majority of the app's functionality will live in core and it is currently mostly tested through instrumentation tests which now reside in app.

Is there a way that instrumentation tests in an application module can generate a coverage report that will include library module sources?

I looked at this question here which bears great similarity to my dilemma but this seems outdated as publishNonDefault is deprecated and does nothing as libraries now publish all variants

My efforts are ongoing on this PR

It will be difficult to complete modularisation with such a drop of coverage, I would expect the coverage to be unchanged post modularisation.

EDIT: I have created a repro project here

macgills
  • 31
  • 7

4 Answers4

1

Eventual answer came from here so all credit to them. Posting the contents of the file here for anybody looking in the future

apply plugin: 'jacoco'

jacoco {
    toolVersion = "$jacocoVersion"
}

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

task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest', 'createDebugCoverageReport']) {

    group "Reporting"
    description "Generate Jacoco coverage reports."

    reports {
        xml.enabled = true
        html.enabled = true
        html.destination file("${rootProject.buildDir}/coverage-report")
    }

    def javaClasses = []
    def kotlinClasses = []
    def javaSrc = []
    def kotlinSrc = []
    def execution = []

    def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']

    rootProject.subprojects.each { proj ->
        javaClasses   << fileTree(dir: "$proj.buildDir/intermediates/javac/debug", excludes: fileFilter)
        kotlinClasses << fileTree(dir: "$proj.buildDir/tmp/kotlin-classes/debug", excludes: fileFilter)
        javaSrc       << "$proj.projectDir/src/main/java"
        kotlinSrc     << "$proj.projectDir/src/main/kotlin"
        execution     << fileTree(dir: proj.buildDir,
                includes: ['jacoco/testDebugUnitTest.exec',
                           'outputs/code_coverage/debugAndroidTest/connected/**/*.ec'])
    }

    sourceDirectories = files([javaSrc, kotlinSrc])
    classDirectories = files([javaClasses, kotlinClasses])

    print execution

    executionData = files(execution)

    doLast() {
        print "file://${reports.html.destination}/index.html"
    }
}

FileFilter probably needs some improvement for a modern Android application eg Dagger/ViewBinding.

I applied this in my app/build.gradle and after running gradlew jacocoTestReport the report with full coverage was present in [projRoot]/build/coverage-report.

Repro project has been updated with the solution.

macgills
  • 31
  • 7
  • The generated report's `index.html` file header is last scanned module name instead of `debugAndroidTest` which was shown on normal coverage reports. By any chance can we change the report heading to `debugAndroidTest` ? – KH_AJU Oct 07 '20 at 11:04
0

See the JacocoMerge task which can merge multiple jacoco execution files into a single file. You can then generate a JacocoReport from the merged exec file

lance-java
  • 25,497
  • 4
  • 59
  • 101
  • This is unfortunately not the solution, I think. I don't want a merged report (codecov handles that for me). I want a report in my `app` module the references code in my `core` module. – macgills Oct 17 '19 at 08:58
  • You can use `JacocoReport.additionalSourceDirs(...)` to add extra sources – lance-java Oct 17 '19 at 09:26
  • I have to write my own task and specify this in it? Sorry but I don't know where this should go, in the jacoco plugin extension I only see `toolsVersion` & `reportsDir` – macgills Oct 17 '19 at 10:21
  • No, see my other answer. You'll configure one of the tasks added by the JaCoCo plugin – lance-java Oct 17 '19 at 18:50
  • `jacocoTestReport` is for unit tests if I am not mistaken? I don't think this has bearing on the instrumentation coverage report run by `createDebugCoverageReport`? I will give it a try in my example project – macgills Oct 18 '19 at 08:41
  • Ah, you are using android. I'm not familiar with the android plugin. It's possible/probable that the JaCoCo tasks are the same type but named differently. You could do `tasks.withType(JacocoReport) {...} ` to configure the tasks instead – lance-java Oct 18 '19 at 10:03
0

No.

Just add your instrumentation tests to the module that has the code under test.

Was under a misconception that instrumentation tests could not be run on a library project due to this SO answer, I have proposed an edit to reflect the up to date documentation.

EDIT: I later found a solution but honestly it probably should not be used and this is the path you should go down. Instrumentation tests probably shouldn't be used for coverage to begin with and unless you are stuck with legacy do not use the solution presented on this page.

macgills
  • 31
  • 7
0

See the JaCoCo plugin docs which show that a task named "jacocoTestReport" is added by the JaCoCo plugin. And you can see there's an "additionalSourceDirs" property on the JacocoReport task

So you can do something like

apply plugin: 'java-library' 
apply plugin: 'jacoco' 
evaluationDependsOn ':another-project'
tasks.withType(JacocoReport) {
   def otherSourceSet = project(':another-project').sourceSets.main
   additionalSourceDirs.from otherSourceSet.allJava
   additionalClassDirs.from otherSourceSet.output
} 
lance-java
  • 25,497
  • 4
  • 59
  • 101
  • Very similar to your answer [here](https://stackoverflow.com/questions/43057920/generate-coverage-for-other-modules). It seems like the custom configuration of the jacocoReport task is a bit more complicated than seen here. [This article](https://medium.com/@android2ee/playing-with-gradle-3-and-some-few-code-coverage-on-android-with-jacoco-47df7c9328ae) goes into more detail but is outdated I think as the location of the `.class` files is not as described – macgills Oct 18 '19 at 09:25
  • See my updates to use `tasks.withType{...}` and to configure `additionalClassDirs` – lance-java Oct 18 '19 at 10:15