apply plugin: 'jacoco'
Since version 0.0.22 Gretty supports Jacoco instrumentation of web-app projects. The resulting coverage information can be converted to report with the help of standard gradle task JacocoReport. Below are the details.
Code coverage of web-apps consists of two parts:
Client-side code coverage covers the code of integration tests (integration tests are typically written with spock + geb or spock + HttpBuilder, but actually it can be any JVM code that connects to your web-app).
Server-side code coverage covers the code of the web-app itself.
Client-side code coverage is not related to Gretty at all. It is activated by applying Jacoco plugin to the given project containing integration tests:
apply plugin: 'jacoco'
Side effects of applying Jacoco plugin:
Jacoco plugin adds JacocoTaskExtension to each Test task of the given project.
Jacoco plugin apppends Jacoco-specific JVM-arguments to each Test task.
When Test task runs, Jacoco gathers code coverage to the file "${project.buildDir}/jacoco/${task.name}.exec"
("task.name" is the name of the test task).
If you defined JacocoReport task, it creates a report from collected code coverage - html, xml or csv.
Here is the example of client-side code coverage configuration:
apply plugin: 'jacoco'
// ...
test {
include '**/Test*.*'
include '**/*Test.*'
exclude '**/*IT.*'
}
task integrationTest(type: Test, dependsOn: 'test') {
outputs.upToDateWhen { false }
finalizedBy { tasks.integrationTestClientReport }
include '**/*IT.*'
jacoco {
append = false
destinationFile = new File(project.buildDir, 'jacoco/integrationTest_client.exec')
}
}
task('integrationTestClientReport', type: JacocoReport) {
executionData project.tasks.integrationTest
sourceDirectories = project.files(project.sourceSets.test.allSource.srcDirs)
classDirectories = project.sourceSets.test.output
def reportDir = project.reporting.file("jacoco/integrationTest_client/html")
reports {
html.destination = reportDir
}
doLast {
println "Jacoco report for client created: file://${reportDir.toURI().path}"
}
}
Server-side code coverage is implemented by Gretty, using Jacoco plugin and task extensions. It is also activated by applying Jacoco plugin:
apply plugin: 'jacoco'
Gretty-specific side effects of applying Jacoco plugin:
Jacoco plugin adds JacocoTaskExtension to each app-start task of the given project - to appRun, appRunDebug, … and, most importantly, to appBeforeIntegrationTest.
By default JacocoTaskExtension is enabled for appBeforeIntegrationTest and disabled for all other app-start tasks. Code coverage is not collected for the tasks with disabled JacocoTaskExtension. You can configure this by setting appRun.jacoco.enabled = true
.
Jacoco plugin apppends Jacoco-specific JVM-arguments to each app-start task.
When app-start task runs, Jacoco gathers code coverage to the file "${project.buildDir}/jacoco/${task.name}.exec"
("task.name" is the name of the app-start task).
If you define JacocoReport task, it creates a report from collected code coverage - html, xml or csv.
Here is the example of server-side code coverage configuration:
apply plugin: 'org.akhikhl.gretty'
apply plugin: 'jacoco'
// ...
gretty {
afterEvaluate {
tasks.appBeforeIntegrationTest.jacoco {
append = false
destinationFile = new File(project.buildDir, 'jacoco/integrationTest_server.exec')
}
tasks.appAfterIntegrationTest.finalizedBy tasks.integrationTestServerReport
}
}
task('integrationTestServerReport', type: JacocoReport) {
executionData { tasks.appBeforeIntegrationTest.jacoco.destinationFile }
sourceDirectories = project.files(project.sourceSets.main.allSource.srcDirs)
classDirectories = project.sourceSets.main.output
def reportDir = project.reporting.file("jacoco/integrationTest_server/html")
reports {
html.destination = reportDir
}
doLast {
println "Jacoco report for server created: file://${reportDir.toURI().path}"
}
}
Note
|
You must configure appBeforeIntegrationTest and appAfterIntegrationTest in gretty.afterEvaluate closure: this is because appBeforeIntegrationTest and appAfterIntegrationTest tasks are not yet defined, when the gradle script is processed. |
Note
|
You cannot pass appBeforeIntegrationTest as a parameter to JacocoReport.executionData for the same reason: it is not yet defined. appBeforeIntegrationTest cannot be wrapped with closure, because internally executionData will pass such closure directly to project.files function and the latter has no idea what to do with task. Wrapping appBeforeIntegrationTest.jacoco.destinationFile with closure will work. |
If you instantiate AppBeforeIntegrationTestTask and AppAfterIntegrationTestTask classes yourself, enabling server-side code coverage looks basically the same:
task('myIntegrationTest', type: Test) {
// ...
}
task('myBeforeIntegrationTest', type: AppBeforeIntegrationTestTask) {
jacoco {
append = false
destinationFile = new File(project.buildDir, 'jacoco/integrationTest_server.exec')
}
integrationTestTask 'myIntegrationTest'
}
task('myAfterIntegrationTest', type: AppAfterIntegrationTestTask) {
finalizedBy { tasks.integrationTestServerReport }
integrationTestTask 'myIntegrationTest'
}
task('integrationTestServerReport', type: JacocoReport) {
executionData tasks.myBeforeIntegrationTest
sourceDirectories = project.files(project.sourceSets.main.allSource.srcDirs)
classDirectories = project.sourceSets.main.output
def reportDir = project.reporting.file("jacoco/integrationTest_server/html")
reports {
html.destination = reportDir
}
doLast {
System.out.println "Jacoco report for server created: file://${reportDir.toURI().path}"
}
}
Note
|
When instantiating AppBeforeIntegrationTestTask and AppAfterIntegrationTestTask classes yourself, you don’t need to wrap task configuration to gretty.afterEvaluate. |
Note
|
Passing myBeforeIntegrationTest as a parameter to JacocoReport.executionData is now simple: task already exists and Jacoco will correctly extract destinationFile from task’s Jacoco extension. |
Tip
|
When instantiating AppBeforeIntegrationTestTask and AppAfterIntegrationTestTask classes yourself, you can use many nice features, like debugging, by setting up task properties. See more information in AppBeforeIntegrationTestTask and AppAfterIntegrationTestTask class documentation. |
Gretty contains the complete and working examples of Jacoco code coverage:
Code coverage with standard Gretty tasks:
https://github.com/akhikhl/gretty/tree/master/examples/jacocoExample
Code coverage with instantiated Gretty tasks:
https://github.com/akhikhl/gretty/tree/master/examples/jacocoInstantiateTasks
Code coverage with multiple web-apps:
https://github.com/akhikhl/gretty/tree/master/examples/farmJacoco
If you get exceptions when using combination JDK-8 + Gradle 1.1x + Jacoco + Gretty, you are very likely hitting a problem of compatibility between JDK-8 and earlier versions of Jacoco. See information on how to fix this here.