'Kotlin Test Coverage

Does anyone know if a good test coverage tool (preferably Gradle plugin) exists for Kotlin? I've looked into JaCoCo a bit, but it doesn't seem to reliably support Kotlin.



Solution 1:[1]

As requested, here is an example build.gradle that uses Kotlin, and incorporates both Jacoco and Sonarqube integration, produces a jar and sources, and ties in Detekt for static analysis.

I had to manually add a couple things as my work build has jacoco applied by an in-house plugin.

plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.2.10'
    id 'org.jetbrains.kotlin.plugin.spring' version '1.2.10'
    id 'org.springframework.boot' version '1.5.9.RELEASE'
    id 'io.spring.dependency-management' version '1.0.4.RELEASE'
    id 'io.gitlab.arturbosch.detekt' version '1.0.0.RC6'
    id "org.sonarqube" version "2.6.2".  
}

apply plugin: 'jacoco'

ext {
    springBootVersion = '1.5.9.RELEASE'
    springCloudVersion = 'Dalston.SR4'
    kotlinVersion = '1.2.10'
    detektVersion = '1.0.0.RC6'.  
}

//======================= Project Info =============================================
group = 'group'
version = '0.14'

dependencyManagement {
    imports {
        mavenBom("org.springframework.boot:spring-boot-starter-        parent:$springBootVersion") 
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:$springCloudVersion"
    }
}

repositories {
    jcenter()
}

//======================= Dependencies =============================================
dependencies {
    // Version MUST be explicitly set here or else dependent projects will not be able to build as Gradle will not know
    // what version to use
    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"

    compile 'org.springframework.boot:spring-boot-starter-web'
    compile('org.springframework.ws:spring-ws-support') {
        exclude(module: 'javax.mail')
    }
    compile 'org.springframework.boot:spring-boot-starter-actuator'

    // Spring security
    compile 'org.springframework.security:spring-security-web'
    compile 'org.springframework.security:spring-security-config'
    compile 'javax.servlet:javax.servlet-api'
    compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml'
    compile('org.apache.httpcomponents:httpclient')
    compile 'com.nimbusds:nimbus-jose-jwt:4.23'
}

//======================= Tasks =============================================
defaultTasks 'build'

tasks.bootRepackage.enabled = false

tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
    kotlinOptions {
        jvmTarget = 1.8
        freeCompilerArgs = ["-Xjsr305=strict"]
    }
}

compileJava.options.encoding = 'UTF-8'

test.testLogging.exceptionFormat = 'full'

// ********************************

ext.coverageExclusions = [
    // Configuration
    'com.bns.pm.config.*',
    // data classes
    'com.bns.pm.domain.*',
    // Account Service domain objects
    'com.bns.pm.account.domain.*',
    // Other items
    'com.bns.pm.exceptions.DataPowerFaultException.Companion',
    'com.bns.pm.controllers.ServiceExceptionHandler*',
    'com.bns.pm.service.callback.DPWebServiceMessageCallback',
    'com.bns.pm.service.HealthCheckService',
    'com.bns.pm.util.SystemPropertiesUtilKt',
]

check.dependsOn jacocoTestCoverageVerification
jacocoTestCoverageVerification {
    violationRules {
        rule {
            element = 'CLASS'
            // White list
            excludes = coverageExclusions
            limit {
                minimum = 0.70
            }
        }
    }
}

jacocoTestReport {
    description 'Generates Code coverage report. Fails build if it does not meet minimum coverage.'

    reports {
        xml.enabled = true    //XML required by coveralls and for the     below coverage checks
        html.enabled = true
        csv.enabled = false
    }

    def reportExclusions = coverageExclusions.collect {
        it.replaceAll('\\.', '/') + (it.endsWith('*') ? '' : '*')
    }
    afterEvaluate {
        classDirectories = files(classDirectories.files.collect {
            fileTree(dir: it, excludes: reportExclusions)
        })
    }
}

test.finalizedBy jacocoTestReport

afterEvaluate {
    sonarqube {
        properties {
            property 'sonar.jacoco.reportPath', "${buildDir}/jacoco/test.exec"
            property "detekt.sonar.kotlin.config.path", "detekt.yml"
            property 'sonar.java.binaries', "$projectDir/build/classes/kotlin"
            property 'sonar.coverage.exclusions', coverageExclusions.collect {
                '**/' + it.replaceAll('\\.', '/') + (it.endsWith('*') ? '' : '*')
            }
        }
    }
}

// Ensure source code is published to Artifactory
// Have to redefine publishing with new name as Accelerator Plugin already defined mavenJava
task sourceJar(type: Jar) {
    from sourceSets.main.allSource
    classifier 'sources'
}

publishing {
    publications {
        mavenJava2(MavenPublication) {
            from components.java
            artifact(sourceJar) {
                classifier = 'sources'
            }
        }
    }
}

artifactory {
    contextUrl = "${artifactory_contextUrl}"
    publish {
        repository {
            repoKey = "${artifactory_projectRepoKey}"
            username = "${artifactory_user}"
            password = "${artifactory_password}"
            maven = true
        }
        defaults {
            publications('mavenJava2')
        }
    }
}

artifactoryPublish {
    dependsOn jar
}

detekt {
    version = detektVersion
    profile("main") {
        input = "$projectDir/src"
        config = "$projectDir/detekt.yml"
        filters = ".*/resources/.*,.*/tmp/.*"
        output = "$project.buildDir/reports/detekt"
    }
}
check.dependsOn detektCheck

Solution 2:[2]

Good news: there is a new kotlinx-kover Gradle plugin, compatible with JaCoCo and IntelliJ.

plugins {
     id("org.jetbrains.kotlinx.kover") version "0.5.0"
}

Once applied, the plugin can be used out of the box without additional configuration.

Watch its YouTube announcement video and also track its roadmap from this youtrack issue.
As said in the video, it solves the problem with inline functions and more.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ThetaSinner
Solution 2