Kover - Gradle plugin for Kotlin code coverage agents: IntelliJ and JaCoCo.
Minimal supported Gradle
version: 6.6
.
- Features
- Quickstart
- Configuration
- Kover single-project tasks
- Kover merged tasks
- Example of configuring Android application
- Implicit plugin dependencies
- Building and contributing
- Collecting code coverage for
JVM
test tasks XML
andHTML
report generation- Support of
Kotlin/JVM
,Kotlin Multiplatform
and mixedKotlin-Java
sources with zero additional configuration Kotlin Android
support without the need to divide into build types and flavours- Customizable filters for instrumented classes
In top-level build file:
Kotlin
plugins {
id("org.jetbrains.kotlinx.kover") version "0.6.1"
}
Groovy
plugins {
id 'org.jetbrains.kotlinx.kover' version '0.6.1'
}
In top-level build file:
Kotlin
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.jetbrains.kotlinx:kover:0.6.1")
}
}
apply(plugin = "kover")
Groovy
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.jetbrains.kotlinx:kover:0.6.1'
}
}
apply plugin: 'kover'
Kover tasks that are created by default designed to collect project coverage only by tests located in the same project.
In order to find the coverage of the project code, the tests for which are in another project, or the coverage of all the code in a multi-project build, it is necessary to calculate the cross-module coverage. Merged tasks are used to calculate such a coverage.
Merged reports are reports that combine statistics of code coverage by test tasks from several projects.
At the same time, for each project, its configuration of instrumentation and special filters (non-class filters) is applied.
For every Gradle project participating in the merged report, both Kover plugin should be applied and Coverage Engine types and versions should be identical.
See how to enable merge reports in this section.
Once applied, the Kover plugin can be used out of the box without additional configuration.
However, in some cases, custom settings are needed - this can be done by configuring special extensions and tasks.
In rare cases, you may need to disable instrumentation for certain classes if it causes execution errors. It may also be convenient to ignore the test task when calculating coverage. You may configure the Kover extension for it.
For example, to configure a standard test task for Kotlin/JVM named test
, you need to add the following code to the build script of the project where this task is declared:
Kotlin
tasks.test {
extensions.configure(kotlinx.kover.api.KoverTaskExtension::class) {
isDisabled.set(false) // true to disable instrumentation tests of this task, Kover reports will not depend on the results of their execution
binaryReportFile.set(file("$buildDir/custom/result.bin")) // set file name of binary report
includes = listOf("com.example.*") // see "Instrumentation inclusion rules" below
excludes = listOf("com.example.subpackage.*") // see "Instrumentation exclusion rules" below
}
}
Groovy
tasks.test {
kover {
disabled = false // true to disable instrumentation tests of this task, Kover reports will not depend on the results of their execution
binaryReportFile.set(file("$buildDir/custom/result.bin")) // set file name of binary report
includes = ['com.example.*'] // see "Instrumentation inclusion rules" below
excludes = ['com.example.subpackage.*'] // see "Instrumentation exclusion rules" below
}
}
For other platforms (like Android or Kotlin-Multiplatform), the names may differ and you may also have several test tasks, so you first need to determine the name of the required task.
Example of configuring test task for build type debug
in Android:
Kotlin
android {
// other Android declarations
testOptions {
unitTests.all {
if (it.name == "testDebugUnitTest") {
it.extensions.configure(kotlinx.kover.api.KoverTaskExtension::class) {
isDisabled.set(false) // true to disable instrumentation tests of this task, Kover reports will not depend on the results of their execution
binaryReportFile.set(file("$buildDir/custom/debug-report.bin")) // set file name of binary report
includes = listOf("com.example.*") // see "Instrumentation inclusion rules" below
excludes = listOf("com.example.subpackage.*") // see "Instrumentation exclusion rules" below
}
}
}
}
}
Groovy
android {
// other Android declarations
testOptions {
unitTests.all {
if (name == "testDebugUnitTest") {
kover {
disabled = false // true to disable instrumentation tests of this task, Kover reports will not depend on the results of their execution
binaryReportFile.set(file("$buildDir/custom/debug-report.bin")) // set file name of binary report
includes = ['com.example.*'] // see "Instrumentation inclusion rules" below
excludes = ['com.example.subpackage.*'] // see "Instrumentation exclusion rules" below
}
}
}
}
}
Instrumentation inclusion rules
Only the specified classes will be instrumented, the remaining (non-included) classes will still be present in the report, but their coverage will be zero.
Instrumentation exclusion rules
The specified classes will not be instrumented and their coverage will be zero.
Instrumentation inclusion/exclusion rules are represented as a fully-qualified name of the class (several classes if wildcards are used).
File or directory names are not allowed.
It is possible to use *
(zero or several of any chars) and ?
(one any char) wildcards. Wildcard **
is similar to the *
.
Examples my.package.ClassName
or my.*.*Name
are allowed, while my/package/ClassName.kt
or src/my.**.ClassName
are not.
Exclusion rules have priority over inclusion ones.
Exclusion and inclusion rules from the test task (if at least one of them is not empty) take precedence over rules from the common class filter.
In the project in which the plugin is applied, you can configure instrumentation and default Kover tasks:
Kotlin
kover {
isDisabled.set(false) // true to disable instrumentation and all Kover tasks in this project
engine.set(DefaultIntellijEngine) // to change engine, use kotlinx.kover.api.IntellijEngine("xxx") or kotlinx.kover.api.JacocoEngine("xxx")
filters { // common filters for all default Kover tasks
classes { // common class filter for all default Kover tasks in this project
includes += "com.example.*" // class inclusion rules
excludes += listOf("com.example.subpackage.*") // class exclusion rules
}
annotations { // common annotation filter for all default Kover tasks in this project
excludes += listOf("com.example.Annotation", "*Generated") // exclude declarations marked by specified annotations
}
}
instrumentation {
excludeTasks += "dummy-tests" // set of test tasks names to exclude from instrumentation. The results of their execution will not be presented in the report
}
xmlReport {
onCheck.set(false) // set to true to run koverXmlReport task during the execution of the check task (if it exists) of the current project
reportFile.set(layout.buildDirectory.file("my-project-report/result.xml")) // change report file name
overrideFilters {
classes { // override common class filter
includes += "com.example2.*" // override class inclusion rules
excludes += listOf("com.example2.subpackage.*") // override class exclusion rules
}
annotations { // override common annotation filter for XML report (filtering will take place only by the annotations specified here)
excludes += listOf("com.example2.Annotation")
}
}
}
htmlReport {
onCheck.set(false) // set to true to run koverHtmlReport task during the execution of the check task (if it exists) of the current project
reportDir.set(layout.buildDirectory.dir("my-project-report/html-result")) // change report directory
overrideFilters {
classes { // override common class filter
includes += "com.example2.*" // class inclusion rules
excludes += listOf("com.example2.subpackage.*") // override class exclusion rules
}
annotations { // override common annotation filter for HTML report (filtering will take place only by the annotations specified here)
excludes += listOf("com.example2.Annotation")
}
}
}
verify {
onCheck.set(true) // set to true to run koverVerify task during the execution of the check task (if it exists) of the current project
rule { // add verification rule
isEnabled = true // set to false to disable rule checking
name = null // custom name for the rule
target = kotlinx.kover.api.VerificationTarget.ALL // specify by which entity the code for separate coverage evaluation will be grouped
overrideClassFilter { // override common class filter
includes += "com.example.verify.*" // override class inclusion rules
excludes += listOf("com.example.verify.subpackage.*") // override class exclusion rules
}
overrideAnnotationFilter { // override common annotation filter (filtering will take place only by the annotations specified here)
excludes += "*verify.*Generated" // declarations marked only by these annotations will be excluded from this rule
}
bound { // add rule bound
minValue = 10
maxValue = 20
counter = kotlinx.kover.api.CounterType.LINE // change coverage metric to evaluate (LINE, INSTRUCTION, BRANCH)
valueType = kotlinx.kover.api.VerificationValueType.COVERED_PERCENTAGE // change counter value (COVERED_COUNT, MISSED_COUNT, COVERED_PERCENTAGE, MISSED_PERCENTAGE)
}
}
}
}
Groovy
kover {
isDisabled.set(false) // true to disable instrumentation and all Kover tasks in this project
engine = kotlinx.kover.api.DefaultIntellijEngine.INSTANCE // // to change engine, use kotlinx.kover.api.IntellijEngine("xxx") or kotlinx.kover.api.JacocoEngine("xxx")
filters { // common filters for all default Kover tasks
classes { // common class filter for all default Kover tasks in this project
includes.add("com.example.*") // class inclusion rules
excludes.addAll("com.example.subpackage.*") // class exclusion rules
}
annotations { // common annotation filter for all default Kover tasks in this project
excludes.addAll("com.example.Annotation", "*Generated") // exclude declarations marked by specified annotations
}
}
instrumentation {
excludeTasks.add("dummy-tests") // set of test tasks names to exclude from instrumentation. The results of their execution will not be presented in the report
}
xmlReport {
onCheck.set(false) // set to true to run koverXmlReport task during the execution of the check task (if it exists) of the current project
reportFile.set(layout.buildDirectory.file("my-project-report/result.xml")) // change report file name
overrideFilters {
classes { // override common class filter
includes.add("com.example2.*") // override class inclusion rules
excludes.addAll("com.example2.subpackage.*") // override class exclusion rules
}
annotations { // override common annotation filter for XML report (filtering will take place only by the annotations specified here)
excludes.addAll("com.example2.Annotation")
}
}
}
htmlReport {
onCheck.set(false) // set to true to run koverHtmlReport task during the execution of the check task (if it exists) of the current project
reportDir.set(layout.buildDirectory.dir("my-project-report/html-result")) // change report directory
overrideFilters {
classes { // override common class filter
includes.add("com.example2.*") // class inclusion rules
excludes.addAll("com.example2.subpackage.*") // override class exclusion rules
}
annotations { // override common annotation filter for HTML report (filtering will take place only by the annotations specified here)
excludes.addAll("com.example2.Annotation")
}
}
}
verify {
onCheck.set(true) // set to true to run koverVerify task during the execution of the check task (if it exists) of the current project
rule { // add verification rule
enabled = true // set to false to disable rule checking
name = null // custom name for the rule
target = 'ALL' // specify by which entity the code for separate coverage evaluation will be grouped
overrideClassFilter { // override common class filter
includes.add("com.example.verify.*") // override class inclusion rules
excludes.addAll("com.example.verify.subpackage.*") // override class exclusion rules
}
overrideAnnotationFilter { // override common annotation filter (filtering will take place only by the annotations specified here)
excludes += "*verify.*Generated" // declarations marked only by these annotations will be excluded from this rule
}
bound { // add rule bound
minValue = 10
maxValue = 20
counter = 'LINE' // change coverage metric to evaluate (LINE, INSTRUCTION, BRANCH)
valueType = 'COVERED_PERCENTAGE' // change counter value (COVERED_COUNT, MISSED_COUNT, COVERED_PERCENTAGE, MISSED_PERCENTAGE)
}
}
}
}
Engine version is specified separately, see specifying coverage engine section.
In order to create a merged report, it has to be enabled explicitly in a containing project with
koverMerged.enable()
or
koverMerged {
enable()
}
By default, merged reports include containing project along with all its subprojects.
Merged reports can also be configured in a similar manner. To do this, you need to configure the extension in the containing project (where koverMerged.enable()
is called)
Kotlin
koverMerged {
enable() // create Kover merged report tasks from this project and subprojects with enabled Kover plugin
filters { // common filters for all default Kover merged tasks
classes { // common class filter for all default Kover merged tasks
includes += "com.example.*" // class inclusion rules
excludes += listOf("com.example.subpackage.*") // class exclusion rules
}
annotations { // common annotation filter for all default Kover merged tasks
excludes += listOf("com.example.Annotation", "*Generated") // exclude declarations marked by specified annotations
}
projects { // common projects filter for all default Kover merged tasks
excludes += listOf("project1", ":child:project") // Specifies the projects excluded from the merged tasks
}
}
xmlReport {
onCheck.set(false) // set to true to run koverMergedXmlReport task during the execution of the check task (if it exists) of the current project
reportFile.set(layout.buildDirectory.file("my-merged-report/result.xml")) // change report file name
overrideClassFilter { // override common class filter
includes += "com.example2.*" // override class inclusion rules
excludes += listOf("com.example2.subpackage.*") // override class exclusion rules
}
overrideAnnotationFilter { // override common annotation filter (filtering will take place only by the annotations specified here)
excludes += "*OverrideGenerated" // declarations marked only by specified annotations will be excluded from merged XML report
}
}
htmlReport {
onCheck.set(false) // set to true to run koverMergedHtmlReport task during the execution of the check task (if it exists) of the current project
reportDir.set(layout.buildDirectory.dir("my-merged-report/html-result")) // change report directory
overrideClassFilter { // override common class filter
includes += "com.example2.*" // override class inclusion rules
excludes += listOf("com.example2.subpackage.*") // override class exclusion rules
}
overrideAnnotationFilter { // override common annotation filter (filtering will take place only by the annotations specified here)
excludes += "*OverrideGenerated" // declarations marked only by specified annotations will be excluded from merged HTML report
}
}
verify {
onCheck.set(true) // set to true to run koverMergedVerify task during the execution of the check task (if it exists) of the current project
rule { // add verification rule
isEnabled = true // set to false to disable rule checking
name = null // custom name for the rule
target = kotlinx.kover.api.VerificationTarget.ALL // specify by which entity the code for separate coverage evaluation will be grouped
overrideClassFilter { // override common class filter
includes += "com.example.verify.*" // override class inclusion rules
excludes += listOf("com.example.verify.subpackage.*") // override class exclusion rules
}
overrideAnnotationFilter { // override common annotation filter (filtering will take place only by the annotations specified here)
excludes += "*verify.*Generated" // declarations marked only by these annotations will be excluded from this rule
}
bound { // add rule bound
minValue = 10
maxValue = 20
counter = kotlinx.kover.api.CounterType.LINE // change coverage metric to evaluate (LINE, INSTRUCTION, BRANCH)
valueType = kotlinx.kover.api.VerificationValueType.COVERED_PERCENTAGE // change counter value (COVERED_COUNT, MISSED_COUNT, COVERED_PERCENTAGE, MISSED_PERCENTAGE)
}
}
}
}
Groovy
koverMerged {
enable() // create Kover merged report tasks from this project and subprojects with enabled Kover plugin
filters { // common filters for all default Kover merged tasks
classes { // common class filter for all default Kover merged tasks
includes.add("com.example.*") // class inclusion rules
excludes.addAll("com.example.subpackage.*") // class exclusion rules
}
annotations { // common annotation filter for all default Kover merged tasks
excludes.addAll("com.example.Annotation", "*Generated") // exclude declarations marked by specified annotations
}
projects { // common projects filter for all default Kover merged tasks
excludes.addAll("project1", ":child:project") // Specifies the projects excluded in the merged tasks
}
}
xmlReport {
onCheck.set(false) // set to true to run koverMergedXmlReport task during the execution of the check task (if it exists) of the current project
reportFile.set(layout.buildDirectory.file("my-merged-report/result.xml")) // change report file name
overrideClassFilter { // override common class filter
includes.add("com.example2.*") // override class inclusion rules
excludes.addAll("com.example2.subpackage.*") // override class exclusion rules
}
overrideAnnotationFilter { // override common annotation filter (filtering will take place only by the annotations specified here)
excludes.addAll("*OverrideGenerated") // declarations marked only by specified annotations will be excluded from merged XML report
}
}
htmlReport {
onCheck.set(false) // set to true to run koverMergedHtmlReport task during the execution of the check task (if it exists) of the current project
reportDir.set(layout.buildDirectory.dir("my-merged-report/html-result")) // change report directory
overrideClassFilter { // override common class filter
includes.add("com.example2.*") // override class inclusion rules
excludes.addAll("com.example2.subpackage.*") // override class exclusion rules
}
overrideAnnotationFilter { // override common annotation filter (filtering will take place only by the annotations specified here)
excludes.addAll("*OverrideGenerated") // declarations marked only by specified annotations will be excluded from merged HTML report
}
}
verify {
onCheck.set(true) // set to true to run koverMergedVerify task during the execution of the check task (if it exists) of the current project
rule { // add verification rule
isEnabled = true // set to false to disable rule checking
name = null // custom name for the rule
target = 'ALL' // specify by which entity the code for separate coverage evaluation will be grouped
overrideClassFilter { // override common class filter
includes.add("com.example.verify.*") // override class inclusion rules
excludes.addAll("com.example.verify.subpackage.*") // override class exclusion rules
}
overrideAnnotationFilter { // override common annotation filter (filtering will take place only by the annotations specified here)
excludes.addAll("*verify.*Generated") // declarations marked only by these annotations will be excluded from this rule
}
bound { // add rule bound
minValue = 10
maxValue = 20
counter = 'LINE' // change coverage metric to evaluate (LINE, INSTRUCTION, BRANCH)
valueType = 'COVERED_PERCENTAGE' // change counter value (COVERED_COUNT, MISSED_COUNT, COVERED_PERCENTAGE, MISSED_PERCENTAGE)
}
}
}
}
Kotlin
kotlinx.kover.api.DefaultIntellijEngine
Groovy
kotlinx.kover.api.DefaultIntellijEngine.INSTANCE
kotlinx.kover.api.IntellijEngine("1.0.683")
Kotlin
kotlinx.kover.api.DefaultJacocoEngine
Groovy
kotlinx.kover.api.DefaultJacocoEngine.INSTANCE
kotlinx.kover.api.JacocoEngine("0.8.8")
Tasks that are created for a project where the Kover plugin is applied:
koverHtmlReport
- Generates code coverage HTML report for all enabled test tasks in the project.koverXmlReport
- Generates code coverage XML report for all enabled test tasks in the project.koverReport
- Executes bothkoverXmlReport
andkoverHtmlReport
tasks.koverVerify
- Verifies code coverage metrics of the project based on configured rules. Always is executed beforecheck
task.
Tasks that are created for project where the Kover plugin is applied and merged reports are enabled:
koverMergedHtmlReport
- Generates code coverage HTML report for all enabled test tasks in all projects.koverMergedXmlReport
- Generates code coverage XML report for all enabled test tasks in all projects.koverMergedReport
- Executes bothkoverMergedXmlReport
andkoverMergedHtmlReport
tasks.koverMergedVerify
- Verifies code coverage metrics of all projects based on specified rules. Always executes beforecheck
task.
Example of configuring test task for build type debug
in Android:
Kotlin
build.gradle.kts
(Project)
buildscript {
// ...
dependencies {
// ...
classpath("org.jetbrains.kotlinx:kover:0.6.1")
}
}
plugins {
id("org.jetbrains.kotlinx.kover") version "0.6.1"
}
koverMerged {
enable()
filters {
classes {
excludes += "*.databinding.*" // exclude classes by mask
}
}
}
build.gradle.kts
(Module)
plugins {
// ...
id("org.jetbrains.kotlinx.kover")
}
android {
// ...
}
dependencies {
// ...
}
kover {
instrumentation {
excludeTasks += "testReleaseUnitTest" // exclude testReleaseUnitTest from instrumentation
}
}
An example is available here
Groovy
build.gradle
(Project)
plugin {
// ...
id 'org.jetbrains.kotlinx.kover' version "0.6.1"
}
koverMerged {
enable()
filters {
classes {
excludes.add "*.databinding.*" // exclude classes by mask
}
}
}
build.gradle
(Module)
plugins {
// ...
id 'org.jetbrains.kotlinx.kover'
}
android {
// ...
}
dependencies {
// ...
}
kover {
instrumentation {
excludeTasks.add "testReleaseUnitTest" // exclude testReleaseUnitTest from instrumentation
}
}
An example is available here
While the plugin is being applied, the artifacts of the JaCoCo or IntelliJ toolkit are dynamically loaded. They are downloaded from the mavenCentral
repository.
For the plugin to work correctly, you need to make sure that the mavenCentral
(or any of its mirrors) is added to the repository list of the project in which the plugin is applied, if it doesn't already exist (usually this is the root project):
Kotlin
repositories {
mavenCentral()
}
Groovy
repositories {
mavenCentral()
}