18

I've been trying to run Jacoco test coverage for quiet some time now. I've tried several possible solutions reported in these topics:

Android test code coverage with JaCoCo Gradle plugin

How do I get a jacoco coverage report using Android gradle plugin 0.10.0 or higher?

Im running the tests in a emulatated device using genymotion. Here is what i added to build.gradle:

apply plugin: 'jacoco'

android{       
    jacoco {
        version "0.7.1.201405082137"
    }        
    buildTypes{
        debug{
                    testCoverageEnabled = true
        }
    }
}

jacoco {
    toolVersion "0.7.1.201405082137"
}

To run it i use something like

./gradlew clean
./gradlew createFLAVOR_NAMEDebugCoverageReport

The relevant generated files/folder are:

/build/intermediates/coverage-instrumented-classes
/build/intermediates/jacoco
/build/outputs/code-coverage/connected/flavors/MyFlavor/coverage.ec

However, there is nothing @ build/reports/jacoco/test/html/index.html or any html page/code coverage report @ /build/outputs.

I've also tried to create a dedicated task to build a coverage report:

def coverageSourceDirs = [
    'src/main/java',
]

task jacocoTestReport(type: JacocoReport, dependsOn: "connectedAndroidTestFLAVOR_NAMEDebug") {
    group = "Reporting"
    description = "Generate Jacoco coverage reports after running tests."
    reports {
        xml.enabled = true
        html.enabled = true
    }
    classDirectories = fileTree(
        dir: './build/intermediates/classes/debug',
        excludes: ['**/R*.class',
                   '**/*$InjectAdapter.class',
                   '**/*$ModuleAdapter.class',
                   '**/*$ViewInjector*.class'
        ])
    sourceDirectories = files(coverageSourceDirs)
    executionData = files("$buildDir/jacoco/connectedAndroidTestMyFlavorDebug.exec")
    // Bit hacky but fixes https://code.google.com/p/android/issues/detail?id=69174.
    // We iterate through the compiled .class tree and rename $$ to $.
    doFirst {
       new File("$buildDir/intermediates/classes/").eachFileRecurse { file ->
            if (file.name.contains('$$')) {
                file.renameTo(file.path.replace('$$', '$'))
            }
        }
    }
}

Then ./gradlew clean and ./gradlew jacocoTestReport. The output is the same as above, so, no html page with coverage report or any other coverage file.

I'm currently using Android Studio v1.0.2 with the latest gradle version. Im fairly new to gradle, so it is possible im missing something basic here.

Thanks

Community
  • 1
  • 1
Adr3nl
  • 455
  • 2
  • 5
  • 17
  • 2
    https://github.com/bumptech/glide/blob/master/library/build.gradle sampl using your tool. – Robert Rowntree Jan 22 '15 at 10:26
  • possible duplicate of [How to get code coverage using Android Studio?](http://stackoverflow.com/questions/18683022/how-to-get-code-coverage-using-android-studio) – 030 Mar 01 '15 at 13:27

2 Answers2

10

After spending the whole day chasing this issue i found out what's the problem. Contrary to the examples i've seen the file generated by the testDebug build is not the .exec file @$buildDir/jacoco/testDebug.exec.

With my gradle and studio version the file generated is a .ec @build/outputs/code-coverage/connected/flavors/myFlavor/coverage.ec

I didn't found any relevant information related to this. It may be a recent change, however, by creating a custom JacocoReport task and changing the executionData variable accordingly i've solved the problem. Here is my implementation:

task jacocoTestReport(type: JacocoReport) {

  def coverageSourceDirs = [
        'src/main/java'
  ]

  group = "Reporting"
  description = "Generates Jacoco coverage reports"
  reports {
      xml{
          enabled = true
          destination "${buildDir}/reports/jacoco/jacoco.xml"
      }
      csv.enabled false
      html{
          enabled true
          destination "${buildDir}/jacocoHtml"
      }
  }

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

  sourceDirectories = files(coverageSourceDirs)
  additionalSourceDirs = files(coverageSourceDirs)
  executionData = files('build/outputs/code-coverage/connected/flavors/smartcompanion/coverage.ec')
}
Adr3nl
  • 455
  • 2
  • 5
  • 17
  • 2
    With all these hints I still have got a code coverage of 0% in the test reports although I run through testing an activity. But my reports are located in ``${buildDir}/reports/coverage/debug`` and not in ``${buildDir}/reports/jacoco/jacoco.xml`` or ``${buildDir}/jacocoHtml``. I already spent a lot of time for integrating Jacoco code coverage in my Instrumentation Tests and I used a lot of descriptions in posts. But still it doesn't run correctly. What's strange is that my ``coverage.ec`` file is empty. – unlimited101 Jul 17 '15 at 11:20
  • Should this snippet exclude all Fragments and Activities? There could be code that needs testing in there. – Theo Sep 08 '15 at 21:34
  • @unlimited101 which device do you use? if using samsung it might be a problem. try using HTC, Motorola or else one – testsingh Nov 13 '18 at 18:52
7

Test coverage report using Jacoco with Android Flavors:

Let's consider you have flavors named "free" and "paid"

  1. Create the file jacoco.gradle in your projects module directory (by default app) where build.gradle exist , it should be next to build.gradle file. directory structure as shown below

    >app > jacoco.gradle
    
  2. Paste below code in the file which we created in Step 1 , The code has self explanatory comments to understand

apply plugin: 'jacoco'

jacoco {
    toolVersion = "0.7.5.201505241946"
}
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
    }
    // When no product flavors defined, use empty
    if (!productFlavors) productFlavors.add('')

    //iterate over the flavors

    productFlavors.each {

        productFlavorName ->
//iterate over build types like debug,release,prod etc.
        buildTypes.each {

            buildTypeName ->
                //sourceName — e.g. freeDebug ,sourcePath — e.g. free/debug
            def sourceName, sourcePath
            if (!productFlavorName) {
                sourceName = sourcePath = "${buildTypeName}"
            } else {
                sourceName = "${productFlavorName}${buildTypeName.capitalize()}"
                sourcePath = "${productFlavorName}/${buildTypeName}"
            }
                // testTaskName —  e.g. testFreeDebugtest task that the coverage task depends on,
            def testTaskName = "test${sourceName.capitalize()}UnitTest"
            // 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")
                reports {
                    //enables and disable the type of file you need
                    xml.enabled = false
                    html.enabled = true
                }
            }
        }
    }
}
  1. run below commands in android studio terminal to build the application

    ./gradlew clean assemble
    
  2. on build successfull , run below command to generate the test report (change the string testFreeDebugUnitTestCoverage to your particular flavor/build type , for example for paid version command will be ./gradlew testPaidDebugUnitTestCoverage)

    ./gradlew testFreeDebugUnitTestCoverage
    
  3. It should give success message in terminal , now go to the directory

    >app > build > reports >jacoco >${testName} >look for html or xml file report file
    
  4. Now you can open and view the html test coverage file in browser

JJD
  • 47,369
  • 53
  • 194
  • 328
Pravin Madavi
  • 71
  • 1
  • 1
  • 197NODMB25385:app tarun$ ./gradlew testFreeDebugUnitTestCoverage bash: ./gradlew: No such file or directory @JJD – Tarun Apr 05 '21 at 06:43