Skip to content

Latest commit

 

History

History
581 lines (467 loc) · 22 KB

README.md

File metadata and controls

581 lines (467 loc) · 22 KB

Kotlinx-Kover

Kotlin Alpha JetBrains incubator project GitHub license

Kover - Gradle plugin for Kotlin code coverage agents: IntelliJ and JaCoCo.

Minimal supported Gradle version: 6.6.

Table of contents

Features

  • Collecting code coverage for JVM test tasks
  • XML and HTML report generation
  • Support of Kotlin/JVM, Kotlin Multiplatform and mixed Kotlin-Java sources with zero additional configuration
  • Kotlin Android support without the need to divide into build types and flavours
  • Customizable filters for instrumented classes

Quickstart

Apply plugin

Applying plugins with the plugins DSL

In top-level build file:

Kotlin
plugins {
     id("org.jetbrains.kotlinx.kover") version "0.6.0-Beta"
}
Groovy
plugins {
    id 'org.jetbrains.kotlinx.kover' version '0.6.0-Beta'
}

Legacy Plugin Application: applying plugins with the buildscript block

In top-level build file:

Kotlin
buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath("org.jetbrains.kotlinx:kover:0.6.0-Beta")
    }
}

apply(plugin = "kover")
Groovy
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.jetbrains.kotlinx:kover:0.6.0-Beta'
    }
}
  
apply plugin: 'kover'    

Cross-project coverage

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.

Configuration

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.

Configuring JVM test task

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 (Android, 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 = 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.

Configuring project

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) // change Coverage Engine
    filters { // common filters for all default Kover tasks
        classes { // common class filter for all default Kover tasks 
            includes += "com.example.*" // class inclusion rules
            excludes += listOf("com.example.subpackage.*") // class exclusion rules
        }
    }

    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) // true to run koverXmlReport task during the execution of the check task
        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
            }
        }
    }

    htmlReport {
        onCheck.set(false) // true to run koverHtmlReport task during the execution of the check task
        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
            }
        }
    }

    verify {
        onCheck.set(true) // true to run koverVerify task during the execution of the check task 
        rule { // add verification rule
            isEnabled = true // 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
            }

            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 // change Coverage Engine
    filters { // common filters for all default Kover tasks
        classes { // common class filter for all default Kover tasks 
          includes.add("com.example.*") // class inclusion rules
          excludes.addAll("com.example.subpackage.*") // class exclusion rules
        }
    }

    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) // true to run koverXmlReport task during the execution of the check task
        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
            }
        }
    }

    htmlReport {
        onCheck.set(false) // true to run koverHtmlReport task during the execution of the check task
        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
            }
        }
    }

    verify {
      onCheck.set(true) // true to run koverVerify task during the execution of the check task 
      rule { // add verification rule
          enabled = true // 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
          }
  
          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.

Configuring merged reports

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 reports
  
    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
        }

        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) // true to run koverMergedXmlReport task during the execution of the check task
        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 
        }
    }

    htmlReport {
        onCheck.set(false) // true to run koverMergedHtmlReport task during the execution of the check task
        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 
        }
    }

    verify {
        onCheck.set(true) // true to run koverMergedVerify task during the execution of the check task 
        rule { // add verification rule
            isEnabled = true // 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
            }
      
            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 reports
  
    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
        }
    
        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) // true to run koverMergedXmlReport task during the execution of the check task
        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 
        }
    }
  
    htmlReport {
        onCheck.set(false) // true to run koverMergedHtmlReport task during the execution of the check task
        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 
        }
    }
  
    verify {
        onCheck.set(true) // true to run koverMergedVerify task during the execution of the check task 
        rule { // add verification rule
            isEnabled = true // 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
            }
      
            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)
            }
        }
    }
}

Specifying Coverage Engine

IntelliJ Coverage Engine with default version

Kotlin
kotlinx.kover.api.DefaultIntellijEngine
Groovy
kotlinx.kover.api.DefaultIntellijEngine.INSTANCE

IntelliJ Coverage Engine with custom version

kotlinx.kover.api.IntellijEngine("1.0.668")

JaCoCo Coverage Engine with default version

Kotlin
kotlinx.kover.api.DefaultJacocoEngine
Groovy
kotlinx.kover.api.DefaultJacocoEngine.INSTANCE

JaCoCo Coverage Engine with custom version

kotlinx.kover.api.JacocoEngine("0.8.8")

Kover single-project tasks

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 both koverXmlReport and koverHtmlReport tasks.
  • koverVerify - Verifies code coverage metrics of the project based on configured rules. Always is executed before check task.

Kover merged tasks

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 both koverMergedXmlReport and koverMergedHtmlReport tasks.
  • koverMergedVerify - Verifies code coverage metrics of all projects based on specified rules. Always executes before check task.

Implicit plugin dependencies

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()
}