Sitemap

Mutation testing in Kotlin using PIT

3 min readJan 30, 2024

This article is continuation of “Mutation testing in Java using PIT”.

The most important thing to look at when setting up PITest for Kotlin project is this repo’s readme: https://github.com/pitest/pitest-junit5-plugin. Otherwise you end up with errors like:

Exception in thread "main" org.pitest.util.PitError: 
Coverage generation minion exited abnormally! (UNKNOWN_ERROR)

Or this one, and many others:

Exception in thread "main" java.lang.IllegalArgumentException: 
Unsupported class file major version 65
at org.pitest.reloc.asm.ClassReader.<init>(ClassReader.java:199)

Here is what to pay attention to:

README.md in pitest-junit5-plugin

Gradle setup for Kotlin codebase

Here is the Gradle build file (in Kotlin). Pay extra attention to what version you set to “info.solidsoft.pitest” and to junit5PluginVersion, as mentioned earlier.

import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED
import org.gradle.api.tasks.testing.logging.TestLogEvent.PASSED
import org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED

plugins {
jacoco
kotlin("jvm") version "1.9.22"
id("info.solidsoft.pitest") version "1.15.0"
}

repositories {
mavenCentral()
}

dependencies {
testImplementation(platform("org.junit:junit-bom:5.10.1"))
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("org.junit.jupiter:junit-jupiter-params")
}

kotlin {
jvmToolchain(21)
}

tasks.test {
useJUnitPlatform()
finalizedBy(tasks.jacocoTestReport)
finalizedBy(tasks.pitest)

testLogging {
events(PASSED, SKIPPED, FAILED)
exceptionFormat = FULL
showExceptions = true
showCauses = true
showStackTraces = true
}
}

tasks.jacocoTestReport {
dependsOn(tasks.test)
}

pitest {
junit5PluginVersion = "1.2.1"
avoidCallsTo = setOf("kotlin.jvm.internal")
mutators = setOf("STRONGER")
targetClasses = setOf("com.example.*")
targetTests = setOf("com.example.*")
threads = Runtime.getRuntime().availableProcessors()
outputFormats = setOf("XML", "HTML")
mutationThreshold = 75
coverageThreshold = 60
}

tasks.named("check") {
dependsOn(":pitest")
}

Write and execute tests

Now we can write tests and implement the code accordingly.

package com.example

import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource

internal class CalculatorTest {

private val calculator = Calculator()

@ParameterizedTest
@ValueSource(ints = [-1, 0, 1, 4, 6, 8, 100])
fun `verifies non primary numbers`(value: Int) {
assertFalse(calculator.isPrime(value))
}

@ParameterizedTest
@ValueSource(ints = [2, 3, 5, 7, 11, 7919])
fun `verifies primary numbers`(value: Int) {
assertTrue(calculator.isPrime(value))
}
}

And we can implement the code as well:

package com.example

class Calculator {

fun isPrime(a: Int): Boolean {
if (a <= 1) {
return false
}
for (i in 2 until a) {
if (a % i == 0) {
return false
}
}
return true
}
}

When we run the tests, we will get JaCoCo report and also PITest coverage report.

JaCoCo coverage report
PITest coverage report

Resources

--

--

No responses yet