Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 610
cjxh Avatar asked Jan 12 '18 06:01

cjxh


Video Answer


2 Answers

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
like image 119
Mikezx6r Avatar answered Sep 18 '22 05:09

Mikezx6r


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

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

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.

like image 40
Mahozad Avatar answered Sep 19 '22 05:09

Mahozad