vendredi 17 janvier 2014

Unit tests and integration tests coverage in sonar with gradle

I recently needed to configure a multi-module gradle project in order to generate unit test, integration test and overall coverage indicators in sonar. As it wasn't so obvious I thought it would be a good idea to share my example.

The goal is to generate the following report in sonar :


Complete gradle file


So here is my complete build.gradle file :

apply plugin: 'sonar-runner'

allprojects {
  apply plugin: 'java'
  apply plugin: 'jacoco'

  sourceSets {
    integtest {
      java {
        compileClasspath += main.output
        runtimeClasspath += main.output
        srcDir 'src/integtest/java'
      }
    }
  }

  configurations {
    integtestCompile.extendsFrom testCompile
  }

  task "integtest"(type: Test, dependsOn: integtestClasses) {
    testClassesDir = sourceSets.integtest.output.classesDir
    classpath = sourceSets.integtest.runtimeClasspath

      jacoco {
          destinationFile = file("$buildDir/jacoco/jacocoIntegTest.exec")
      }
  }

  test {
    jacoco {
      destinationFile = file("$buildDir/jacoco/jacocoTest.exec")
    }
  }

  sonarRunner {
    sonarProperties {
      property "sonar.jacoco.reportPath", "$buildDir/jacoco/jacocoTest.exec"
      property "sonar.jacoco.itReportPath", "$buildDir/jacoco/jacocoIntegTest.exec"
    }
  }
}

sonarRunner {
    sonarProperties {
        property "sonar.projectName", "test-samples"
        property "sonar.projectKey", "jsebfranck.samples"
    }
}

Explanations

I used Jacoco to generate my tests coverage indicators :

  apply plugin: 'jacoco'


The following lines allow to configure my integration tests. To distinguish these tests from the unit tests, I moved them in a specific source folder : "src/integtest/java". Please note that with gradle, the default test folder is "src/test/java" and I use it for my unit tests.

  sourceSets {
    integtest {
      java {
        compileClasspath += main.output
        runtimeClasspath += main.output
        srcDir 'src/integtest/java'
      }
    }
  }

  configurations {
    integtestCompile.extendsFrom testCompile
  }

  task "integtest"(type: Test, dependsOn: integtestClasses) {
    testClassesDir = sourceSets.integtest.output.classesDir
    classpath = sourceSets.integtest.runtimeClasspath
  }

I configured Jacoco to generate the tests coverage data in a specific directory : one for the unit tests and for the integration tests :

  task "integtest"(type: Test, dependsOn: integtestClasses) {
    testClassesDir = sourceSets.integtest.output.classesDir
    classpath = sourceSets.integtest.runtimeClasspath

      jacoco {
          destinationFile = file("$buildDir/jacoco/jacocoIntegTest.exec")
      }
  }

  test {
    jacoco {
      destinationFile = file("$buildDir/jacoco/jacocoTest.exec")
    }
  }

Then for each module, I configured sonar to use generated Jacoco data :
  • reportPath property allows to configure the unit tests coverage
  • itReportPath property allows to configure the integration tests coverage
allprojects {
  sonarRunner {
    sonarProperties {
      property "sonar.jacoco.reportPath", "$buildDir/jacoco/jacocoTest.exec"
      property "sonar.jacoco.itReportPath", "$buildDir/jacoco/jacocoIntegTest.exec"
    }
  }
}

Finally I configured my sonar instance. Here I use the default parameters so the report is generated on my localhost sonar installation and its h2 database :

sonarRunner {
    sonarProperties {
        property "sonar.projectName", "test-samples"
        property "sonar.projectKey", "jsebfranck.samples"
    }
}


Source code

Please find the complete source code on my github repository about tests.



4 commentaires:

  1. Hi! Thank you for the article!
    How can I do the same for
    1) manual invocation of jacocoTestReport ?
    2) in Jenkins ?

    RépondreSupprimer
  2. Hi,

    For a manual invocation of jacocoTestReport, you must add the following lines in your build.gradle file :

    jacocoTestReport {
    reports {
    html.enabled = true
    html.destination "${buildDir}/jacocoHtml"
    }
    }

    -> It will generate the report in a jacocoHtml directory.

    And in jenkins, in order to update sonar data, you have to launch the task "gradle test integTest jacoco sonarRunner".

    Cheers!

    RépondreSupprimer
    Réponses
    1. Hi i added jacocoTestReport {
      reports {
      html.enabled = true
      html.destination "${buildDir}/jacocoHtml"
      }
      }
      but i am not able to see jacocoHtml directory why?

      Supprimer
  3. Hi,

    Can you please tell me, does your reports let you see details like what are the lines in a class that are covered? I am having some issues in this sense as only the coverage percent is shown?

    I'd really appreciate a reply.

    Thank you

    RépondreSupprimer