Jacoco code coverage with Gradle

One of the new features of Gradle 1.6, the Groovy based build tool, is Jacoco support. Jacoco is one of the few (only?) Open Source code coverage tools still in active development, and starting from Gradle 1.6, it’s now a supported plugin. Enabling the task is easy, just add

to your build.gradle, and run ./gradlew jacocoTestReport and it.. does nothing. For those running into the same issues, this might save you a bit of time:

After a bit of searching, it turns out the Jacoco plugin is a bit broken. When using it with a Java project, as I’m doing, you need to do a couple of things to enable it, but also then, it unfortunately doesn’t work as advertised, though the fix seems not to complex.

First, you need to enable the following:

And then, instead of running ./gradlew jacocoTestReport as mentioned in the docs here, you need to run ./gradlew test jacocoTestReport. Unfortunately, no location is printed where the documentation is created, so no clickable links in your terminal, but you can find it under build/reports/jacoco/test/html/index.html. Well, that wasn’t so bad now, was it? Have fun!

More resources

For more information about using Jacoco and Gradle, you can checkout the following resources:

19 replies
  1. Arun
    Arun says:

    Hi,

    Could you please clarify the following:

    1. you example shows to add the following in a project/module’s build.gradle file.

    jacocoTestReport {
    group = “Reporting”
    description = “Generate Jacoco coverage reports after running tests.”
    additionalSourceDirs = files( sourceSets.main.allJava.srcDirs)}

    Does, the above code needs to be copied exactly or Do i have to change the value of “sourceSets.main.allJava.srcDirs” with some relative path/folder i.e. src/java/main or something similar.

    2. Gradle documentation says to include this, which is different that what you have mentioned. Then, do I need to add the lines that you have specified above or just replace whatever Gradle/Jacoco documentation says?

    jacocoTestReport{
    reports {
    xml.enabled false
    csv.enabled false
    html.destination “${buildDir}/jacocoHtml”
    }
    }

    3. I dont see jacoco folder or it’s jar anywhere in the workspace, I assume enabling apply plugin: ‘jacoco’ in build.gradle should do it.

    4. Im new to gradle and doc seems like pretty easy to configure but a good video based example (showing how to get a coverage report on an App or service … where an app is dependent upon service(s) and has junit tests, integration tests within the source code tree) would help a lot.

    thanks a lot.

    • Erik Pragt
      Erik Pragt says:

      Hi Arun,

      Regarding 1, it’s an exact copy. Don’t make changes to it, it’s code which will be executed by Gradle.

      Regarding 2, it’s something different. If you don’t want the xml and csv, use your suggestion, but you’ll currently need mine to get the plugin to work.

      Regarding 3: which task do you run? And what do you mean with ‘workspace’?

      Regarding 4: There are many good resources to be found on Gradle, including screencasts, video’s, webinars, etc. Some of them can be found here

      Hope that helps,

      Erik

      • Arun
        Arun says:

        Thanks for your quick reply Erik. For your reply to my question – bullet3: I’m running:

        gradle clean build check test JacocoTestReport

        When I run gradlew, it doesn’t do anything.

        • Erik Pragt
          Erik Pragt says:

          Hi Arun,

          Sure, no problem. Is that really the command you are using? Because it’s: jacocoTestReport, with a lower case j, not with the upper case. That might help.

          Erik

  2. Arun
    Arun says:

    Apart from that: gradle jacocoTestReport

    is working but I’m not seeing any build/reports folder getting generated.

    /src/java – contains java code in the tree

    /src/java-test – contains junit tests

      • Arun
        Arun says:

        Yep, i’m using jacocoTestReport. Tried the following:

        gradle jacocoTestReport
        (says build successful) but nothing under build folder for Jacoco related data.

        gradle test jacocoTestReport
        (fails when running from command line. In jenkins i tell to get the dependencies from Artifactory so there no problem but code coverage data is absent.. no build/… files for jacoco or report/jacoco etc.

        I’m getting the following in Jenkins logs:

        20:29:12 :jacocoTestReport
        20:29:12 Skipping task ‘:jacocoTestReport’ as task onlyIf is false.
        20:29:12 :jacocoTestReport SKIPPED

        then later for Jenkins’s jacoco report publishing step… log says:

        20:29:16 [JaCoCo plugin] Collecting JaCoCo coverage data…
        20:29:16 [JaCoCo plugin] **/**.exec;**/classes;**/src/main/java; locations are configured
        20:29:16 [JaCoCo plugin] Number of found exec files: 0
        20:29:16 [JaCoCo plugin] Saving matched execfiles:
        20:29:16 [JaCoCo plugin] Saving matched class directories: /production/jenkinsAKS/jobs/NeoApplication/workspace/bin/tomcat/webapps/NeoApplication/WEB-INF/classes /production/jenkinsAKS/jobs/NeoApplication/workspace/build/classes /production/jenkinsAKS/jobs/NeoApplication/workspace/tomcat/webapps/NeoApplication/WEB-INF/classes
        20:29:16 [JaCoCo plugin] Saving matched source directories: /production/jenkinsAKS/jobs/NeoApplication/workspace/jacoco-0.6.3.201306030806/doc/examples/build/src/main/java
        20:29:16 [JaCoCo plugin] Loading inclusions files..
        20:29:16 [JaCoCo plugin] inclusions: []
        20:29:16 [JaCoCo plugin] exclusions: []
        20:29:16 [JaCoCo plugin] Thresholds: JacocoHealthReportThresholds [minClass=0, maxClass=0, minMethod=0, maxMethod=80, minLine=0, maxLine=80, minBranch=0, maxBranch=70, minInstruction=0, maxInstruction=80, minComplexity=0, maxComplexity=0]
        20:29:16 [JaCoCo plugin] Publishing the results..
        20:29:16 [JaCoCo plugin] Loading packages..
        20:29:16 [JaCoCo plugin] Done.
        20:29:17 Finished: SUCCESS

        I have an unzipped copy of “jacoco-0.6.x.x.x.x.x” folder under my workspace (i.e. source code checked out from version control) folder location, im pretty sure that folder is not even getting used. So far, coverage is 0%.

  3. Arun
    Arun says:

    Got it working.

    Gradle 1.7
    – download the .zip which contains the binaries/src and documentation.
    – Go to folder: if you unzip the above .zip at C:gradle-1.7
    C:gradle-1.7samplestestingjacocoquickstart

    – Run:
    gradle build jacocoTestReport

    – You’ll see a new folder “build” after the build.
    – folder jacoco gets created with classdumps and .exec if only build task is called.
    – folder jacoco and jacocoHtml gets created – if both build jacocoTestReport is called

    have fun.

    I also saw that it’s better to include:

    the following section in build.gradle

    /////
    tasks.withType(Compile) {
    options.debug = true
    options.compilerArgs = [“-g”]
    }
    ////

  4. Mike DuVall
    Mike DuVall says:

    Got it working with my project. Thanks.

    Question: Does anyone know of a way to make it fail the build if a required level of code coverage is not met? (Something like cobertura’s ‘cobertura-check’ ant target)

  5. Gargi Agrawal
    Gargi Agrawal says:

    Hi Erik,

    I saw your post and I added the task which you had mentioned but still I am getting below logs. Please let me know where I am going wrong.

    when I run: gradle test jacocoTestReport —->

    :CCL-CACHE:jacocoTestReport SKIPPED
    :CCL-CDAL-API:jacocoTestReport SKIPPED
    :CCL-COMMON-VO:jacocoTestReport SKIPPED
    :CCL-CORE:jacocoTestReport SKIPPED

    Below is the snippet of my build.gradle:

    subprojects {

    apply plugin: ‘osgi’
    apply plugin: ‘maven’
    apply plugin: ‘jacoco’
    apply plugin: ‘java’

    repositories {
    mavenCentral()
    }

    dependencies {
    testCompile ‘junit:junit:4.8.2’
    }

    /*
    * defines the gradle wrapper version to use for building
    */
    task wrapper(type: Wrapper) {
    description = ‘Installs the gradle wrapper.’
    gradleVersion = ‘1.10’
    }

    tasks.withType(Compile) {
    options.debug = true
    options.compilerArgs = [“-g”]
    }

    jacoco {
    toolVersion = “0.6.2.201302030002”
    reportsDir = file(“$buildDir/reports”)
    }

    test {
    jacoco {
    append = false
    destinationFile = file(“$buildDir/jacoco/jacocoTest.exec”)
    classDumpFile = file(“$buildDir/jacoco/classpathdumps”)
    }
    }

    sourceSets {
    main {
    java {
    srcDir ‘src/main/java’
    }
    }
    test {
    java {
    srcDir ‘test/main/java’
    }
    }

    }

    jacocoTestReport {
    group = “Reporting”
    description = “Generate Jacoco coverage reports after running tests.”
    reports {
    xml{
    enabled true
    destination “${buildDir}/reports/jacoco/jacoco.xml”
    }
    csv.enabled false
    html{
    enabled true
    destination “${buildDir}/jacocoHtml”
    }
    }
    additionalSourceDirs = files(sourceSets.main.allJava.srcDirs)
    }
    }

    • Arun Sangal
      Arun Sangal says:

      Gargi, that’s most probably because jacocoTestReport don’t have anything (source data to work one which in this case would be .exec files for Unit / Integration or any other tests). Do you see .exec files generated by Gradle (when test or integrationTest etc tasks are run). See build/jacoco/.. folder tree.

      jacoco {
      //toolVersion = “0.6.2.201302030002”
      toolVersion = “0.7.0.201403182114”
      // reportsDir = file(“$buildDir/customJacocoReportDir”)
      }

      test {
      maxParallelForks = 5
      forkEvery = 50
      ignoreFailures = true

      testReportDir = file(“$buildDir/reports/tests/UT”)
      testResultsDir = file(“$buildDir/test-results/UT”)

      //Following Jacoco test section is required only in Jenkins instance extra common file
      jacoco {
      //The following vars works ONLY with 1.6 of Gradle
      destPath = file(“$buildDir/jacoco/UT/jacocoUT.exec”)
      classDumpPath = file(“$buildDir/jacoco/UT/classpathdumps”)

      //Following vars works only with versions >= 1.7 version of Gradle
      //destinationFile = file(“$buildDir/jacoco/UT/jacocoUT.exec”)
      // classDumpFile = file(“$buildDir/jacoco/UT/classpathdumps”)
      }
      }

      task integrationTest( type: Test) {
      //Always run tests
      outputs.upToDateWhen { false }
      ignoreFailures = true

      testClassesDir = sourceSets.integrationTest.output.classesDir
      classpath = sourceSets.integrationTest.runtimeClasspath

      testReportDir = file(“$buildDir/reports/tests/IT”)
      testResultsDir = file(“$buildDir/test-results/IT”)

      //Following Jacoco test section is required only in Jenkins instance extra common file
      jacoco {
      //This works with 1.6
      destPath = file(“$buildDir/jacoco/IT/jacocoIT.exec”)
      classDumpPath = file(“$buildDir/jacoco/IT/classpathdumps”)

      //Following works only with versions >= 1.7 version of Gradle
      //destinationFile = file(“$buildDir/jacoco/IT/jacocoIT.exec”)
      // classDumpFile = file(“$buildDir/jacoco/IT/classpathdumps”)
      }
      }

      jacocoTestReport {
      group = “Reporting”
      description = “Generate Jacoco coverage reports after running tests.”
      ignoreFailures = true

      //executionData = files(‘build/jacoco/UT/jacocoUT.exec’)
      //executionData = files(‘build/jacoco/IT/jacocoIT.exec’)
      //executionData = files(‘build/jacoco/UT/jacocoUT.exec’, ‘build/jacoco/IT/jacocoIT.exec’)

      //executionData = files([‘build/jacoco/UT/jacocoUT.exec’, ‘build/jacoco/IT/jacocoIT.exec’])
      executionData = fileTree(dir: ‘build/jacoco’, include: ‘**/*.exec’)

      reports {
      xml{
      enabled true
      //Following value is a file
      destination “${buildDir}/reports/jacoco/xml/jacoco.xml”
      }
      csv.enabled false
      html{
      enabled true
      //Following value is a folder
      destination “${buildDir}/reports/jacoco/html”
      }
      }

      sourceDirectories = files(sourceSets.main.allJava.srcDirs)
      classDirectories = files(sourceSets.main.output.classesDir)
      //classDirectories = fileTree(dir: ‘build/classes’)

      additionalSourceDirs = files([‘test/java’, ‘src/java-test’])
      //additionalSourceDirs = files(sourceSets.test.allJava.srcDirs)
      //additionalSourceDirs += files(sourceSets.integrationTest.allJava.srcDirs)
      //additionalSourceDirs += files(sourceSets.acceptanceTest.allJava.srcDirs)
      // additionalClassDirs = files([‘build/jacoco/UT/classpathdumps/com/thc’, ‘build/jacoco/IT/classpathdumps/com/thc’])
      //additionalClassDirs = files([‘build/jacoco/UT/classpathdumps/com/thc’])

      //additionalSourceDirs = files(‘test/java’, ‘src/java-test’)
      // additionalClassDirs = files(‘build/jacoco/UT/classpathdumps/com/thc’, ‘build/jacoco/IT/classpathdumps/com/thc’)

      //sourceDirectories = files(‘src/main/java’)
      // classDirectories = files(‘build/classes/main’)

      //sourceDirectories = fileTree(‘src/main/java’)
      // classDirectories = fileTree(‘build/classes/main’)

      //additionalSourceDirectories = fileTree(‘test/java’, ‘test/resources’, ‘src/java-test’)
      // additionalClassDirectories = fileTree(‘build/classes/test’, ‘build/classes/integrationTest’)

      //additionalSourceDirs = files(sourceSets.test.allJava.srcDirs,sourceSets.integrationTest.allJava.srcDirs)
      //additionalSourceDirs = files(sourceSets.*.allJava.srcDirs)
      //additionalSourceDirs += files(sourceSets.test.allJava.srcDirs)
      //additionalSourceDirs += files(sourceSets.integrationTest.allJava.srcDirs)

      //additionalSourceDirs = files(‘src/java’, ‘test/java’, ‘test/resources’, ‘src/java-test’, ‘conf’)
      //additionalClasseDirs = files(‘build/classes’)
      //executionData = files(‘build/jacoco/test.exec’, ‘build/jacoco/integrationTest.exec’, ‘build/jacoco/acceptanceTest.exec’)

      //additionalSourceDirs = files(sourceSets.integrationTest.allSource.srcDirs)

      //additionalSourceDirs = files(‘src/java’, ‘test/java’, ‘test/resources’, ‘src/java-test’, ‘conf’)
      //additionalSourceDirs = files(sourceSets.test.allJava.srcDirs)
      //additionalSourceDirs = files(sourceSets.integrationTest.allJava.srcDirs)
      //additionalSourceDirs = files([sourceSets.main.allJava.srcDirs)
      }

Comments are closed.