skip to content

What Gradle/source-set setup is required to write kotlin.test tests in common code for a multiplatform module, and how do you pick the JVM test runner?

level: middleimportance: should knowfreq 30%

answer

  1. implementation(kotlin("test")) in commonTest
  2. plugin auto-wires per-target runners
  3. useJUnitPlatform() for JUnit 5 on jvmTest
  4. src/commonTest vs src/jvmTest split
  5. ./gradlew allTests aggregates targets

basics

~20 s

Add the kotlin-test dependency to the commonTest source set and put your tests there using kotlin.test imports. For the JVM you pick JUnit 4 or 5 either by a small dependency or by calling useJUnitPlatform() on the test task.

solid answer

~40 s

In a Kotlin Multiplatform build, you declare a dependency on `kotlin("test")` in the `commonTest` source set; the Kotlin Gradle plugin automatically wires the matching platform test runners (JUnit on JVM, the JS runner, Kotlin/Native runner). Your shared tests live in `src/commonTest/kotlin` using `kotlin.test` annotations and assertions. For the JVM runner choice: with newer Kotlin versions JUnit 4 is the default for the JVM target, but you opt into JUnit Platform/Jupiter by configuring the JVM test task with `useJUnitPlatform()` (and/or depending on `kotlin-test-junit5`). Platform-specific tests go in their own source sets like `src/jvmTest`, `src/jsTest`, `src/nativeTest`, where you may use framework-specific features not available in common.

go deeper

for a junior

Knows tests go in commonTest and need the kotlin test dependency.

for a middle

Configures kotlin("test") in commonTest and selects the JVM runner with useJUnitPlatform().

for a senior

Explains source-set boundaries, when to drop to jvmTest, and the runner-selecting artifacts.

for a principal

Reasons about CI aggregation (allTests), report wiring, and keeping the common surface portable across targets.

## Source set layout A KMP module has source sets named `<target>Main` and `<target>Test`, plus the shared `commonMain`/`commonTest`: ``` src/ commonMain/kotlin/ <- shared production code commonTest/kotlin/ <- shared tests (kotlin.test) jvmMain/kotlin/ jvmTest/kotlin/ jsMain/kotlin/ jsTest/kotlin/ nativeMain/kotlin/ nativeTest/kotlin/ ``` ## The one dependency you need ```kotlin kotlin { jvm() js(IR) { nodejs() } // native targets... sourceSets { commonTest.dependencies { implementation(kotlin("test")) // pulls kotlin-test for every target } } } ``` `kotlin("test")` resolves to the multiplatform `kotlin-test` artifact. The Kotlin Gradle plugin **auto-aligns** the per-target backings: JUnit for JVM, the JS test runner, and the Kotlin/Native runner. You do **not** add JUnit by hand for the basic case. ## Choosing the JVM runner The JVM target can run on JUnit 4 or the JUnit Platform (Jupiter / JUnit 5): ```kotlin // JUnit Platform / Jupiter on the JVM target: kotlin { jvm() } tasks.named<Test>("jvmTest") { useJUnitPlatform() } // and/or depend on kotlin-test-junit5 in jvmTest ``` - **`useJUnitPlatform()`** switches the Gradle `Test` task to the JUnit Platform launcher (needed for JUnit 5). - The `kotlin-test-junit` vs `kotlin-test-junit5` artifact selects which framework kotlin.test's JVM annotations alias to. With recent Kotlin, the plugin defaults the JVM target to JUnit 4 unless you opt into the platform. ## Where platform-specific tests live If a test needs JVM-only APIs (files, threads) or JUnit 5 features (`@ParameterizedTest`, `@Nested`), put it in `src/jvmTest` and import the framework directly. Tests in `commonTest` must stay platform-neutral. ## Running - `./gradlew allTests` — runs every target's tests and aggregates a report. - `./gradlew jvmTest`, `jsTest`, `nativeTest` — run a single target. ## Common mistake Adding `org.junit.jupiter:junit-jupiter` to `commonTest` — it won't compile for JS/Native. JUnit dependencies belong only in `jvmTest` (or are pulled transitively by `kotlin("test")` for the JVM target).

  • Where does a test that needs java.io.File go?
    In src/jvmTest, not commonTest — file APIs are JVM-only, so the test can't compile for JS/Native.
  • What does ./gradlew allTests do?
    It runs the test tasks for all configured targets and produces an aggregated multiplatform test report.

saying these in an interview costs you the question

  • Adding junit-jupiter directly to commonTest
  • Manually adding kotlin-test per target instead of once in commonTest
  • Forgetting useJUnitPlatform() then wondering why JUnit 5 tests don't run
  • Putting JVM-only tests in commonTest and breaking the JS/Native build

context