Today, I’ll show you how to configure Kotest for iOS, Linux, JVM and Android on a KMP project.
We’ll focus on the Gradle configuration. You can find the full template in this repository: Kotest-KMP-Setup
The base config (Native targets)
First, we need the Kotest and KSP gradle plugin.
plugins {
id("org.jetbrains.kotlin.multiplatform")
id("com.android.kotlin.multiplatform.library")
id("io.kotest")
id("com.google.devtools.ksp")
}
And then, add the kotest engine as a dependency in commonTest.
kotlin {
iosX64()
iosArm64()
iosSimulatorArm64()
linuxX64()
sourceSets {
commonTest {
dependencies {
implementation("io.kotest:kotest-framework-engine:<kotest-version>")
}
}
}
}
This setup is enough for iOS and Linux targets. You can already add kotest specs on commonTest or one of the native test source sets and execute them with ./gradlew allTests, or execute them for a single target, like: ./gradlew iosSimulatorArm64Test.
JVM
Kotest offers 2 ways for running tests on the JVM. Using the kotest engine or JUnit Platform. Choose one of them.
Kotest engine
The kotest gradle plugin we added before already sets this up once you add the JVM target:
kotlin {
jvm()
}
But depending on the gradle version you are using, you will probably see errors when trying to run the tests, saying that test task did not discover any tests to execute. You can fix this with the following setup:
kotlin {
jvm {
testRuns.all {
executionTask {
failOnNoDiscoveredTests = false
}
}
}
}
This will give you a new jvmKotest task, not to be confused with the standard jvmTest task. That task is not wired to the allTests task, so you can manually set this dependency up:
tasks.named("allTests") {
dependsOn("jvmKotest")
}
JUnit platform
This requires setting up JVM test tasks to run on the JUnit platform:
kotlin {
jvm {
testRuns.all {
executionTask {
useJUnitPlatform()
}
}
}
}
And adding the following dependency to jvmTest.
kotlin {
sourceSets {
jvmTest {
dependencies {
implementation("io.kotest:kotest-runner-junit5:<kotest-version>")
}
}
}
}
Kotest tests will run with the standard jvmTest.
Android
Android is currently a bit of a special case. We can only run tests with the JUnit Platform, no support for the kotest engine.
Also, The new plugin (com.android.kotlin.multiplatform.library) doesn’t yet provide a clean DSL to configure unit tests like the deprecated (com.android.library) plugin did.
In the old plugin, we could do this:
// This does NOT work with the new KMP Android library plugin
android.testOptions {
unitTests.all { it.useJUnitPlatform() }
}
Because that API is missing, we have to configure the task directly by its name. Interestingly, tasks.named("testAndroidHostTest") often fails to resolve during the configuration phase in this specific setup, so using tasks.withType<Test> { } with a name check is the most reliable workaround.
kotlin {
androidLibrary {
// other android configs
withHostTest { } // enable unit tests (now called host tests)
}
}
tasks.withType<Test> {
if (name == "testAndroidHostTest") {
useJUnitPlatform()
}
}
And adding the same lib we added for JVM on the androidHostTest source set.
kotlin {
sourceSets {
getByName("androidHostTest") {
dependencies {
implementation("io.kotest:kotest-runner-junit5:<kotest-version>")
}
}
}
}
Running the tests
Once configured, you have two main ways to run your suite:
./gradlew allTests: Runs all multiplatform tests (including the manually wired jvmKotest)../gradlew check: The standard Gradle lifecycle task that runs all tests and verification tasks (lint, etc.).