skip to content

How Kotlin Does Testing

Kotlin testing layers a multiplatform assertion API, a mocking library built for Kotlin's final-by-default classes, an optional spec-style framework, and virtual-time tools for coroutines. Knowing which tool solves which problem is the point of this area.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

6

What is the kotlin.test library, and how do you write a basic assertion-based unit test with it?

level: juniorimportance: must knowfreq 70%

answer

  1. kotlin.test = annotations + assertions, not a runner
  2. @Test, @BeforeTest, @AfterTest, @Ignore
  3. assertEquals(expected, actual) — expected first
  4. assertFailsWith<T> returns the exception
  5. Multiplatform: compiles in commonTest, JUnit runs it on JVM

basics

~10 s

kotlin.test is Kotlin's small built-in testing toolkit. You mark a function with @Test, then call assertions like assertEquals or assertTrue to check that your code produced the right result.

solid answer

~30 s

kotlin.test is a thin, multiplatform assertion and annotation library. It provides annotations (@Test, @BeforeTest, @AfterTest, @Ignore) and assertion functions (assertEquals, assertTrue, assertFalse, assertNull, assertNotNull, assertSame, assertFailsWith, fail). On JVM these annotations are mapped onto an underlying engine (usually JUnit) via kotlin-test-junit5, so JUnit actually runs them; on JS/Native they map to platform runners. Crucially, assertEquals(expected, actual) takes expected FIRST. assertFailsWith<T> verifies an exception type is thrown and returns it for further assertions. Because the API is common across platforms, the same test source compiles in a Kotlin Multiplatform commonTest source set. You typically run tests via Gradle's `test` task.

go deeper

for a junior

Can write an @Test method and use assertEquals/assertTrue correctly with proper argument order.

for a middle

Knows assertFailsWith, assertNotNull smart-cast, and that kotlin.test delegates to JUnit on the JVM.

for a senior

Explains the multiplatform delegation model and when to layer MockK/Kotest on top of the thin API.

for a principal

Reasons about commonTest source-set strategy and choosing kotlin.test vs Kotest assertions as a team-wide standard.

## What kotlin.test Is `kotlin.test` is Kotlin's own lightweight testing API. It is **not** a test runner by itself — it is a set of **annotations** and **assertion functions** with a common API that works across Kotlin Multiplatform targets (JVM, JS, Native, Wasm). On each platform it delegates to a real engine; on the JVM you add `kotlin-test-junit5` so JUnit 5 actually discovers and runs the tests. ## Core Annotations - `@Test` — marks a test function. - `@BeforeTest` / `@AfterTest` — run before/after each test (setup/teardown). - `@Ignore` — skip a test. ## Core Assertions - `assertEquals(expected, actual, message?)` — **expected comes first**. - `assertTrue(condition)`, `assertFalse(condition)`. - `assertNull(value)`, `assertNotNull(value)` — the latter smart-casts to non-null and returns it. - `assertSame` / `assertNotSame` — reference identity. - `assertFailsWith<T>(block)` — asserts the block throws `T`, returns the exception. - `fail(message)` — force a failure. ```kotlin import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith class CalculatorTest { @Test fun `adds two numbers`() { assertEquals(5, add(2, 3)) // expected, actual } @Test fun `divide by zero throws`() { val ex = assertFailsWith<ArithmeticException> { divide(1, 0) } assertEquals("/ by zero", ex.message) } } ``` ## Why Use It - **Multiplatform**: the same `commonTest` code compiles everywhere. - **Idiomatic Kotlin**: backtick test names, `assertNotNull` smart-cast. - **Thin**: for richer matchers/specs you bring MockK or Kotest on top.

  • Why does kotlin.test need kotlin-test-junit5 on the JVM?
    kotlin.test only defines the API; it has no runner. kotlin-test-junit5 maps its annotations onto JUnit 5 so the JUnit Platform actually discovers and executes the tests.
  • What is the argument order for assertEquals and why does it matter?
    assertEquals(expected, actual). Getting it backwards still passes/fails correctly but produces misleading 'expected X but was Y' messages, confusing whoever reads the failure.

saying these in an interview costs you the question

  • Thinking kotlin.test is a standalone runner like JUnit
  • Putting actual before expected in assertEquals
  • Confusing assertEquals (value) with assertSame (identity)
  • Using try/catch + fail() instead of assertFailsWith
  • Believing kotlin.test only works on the JVM

context

open as a page

How does MockK let you create and verify mocks in Kotlin, and why is it preferred over Mockito for Kotlin code?

level: middleimportance: must knowfreq 68%

basics

~20 s

MockK is a Kotlin mocking library. You create a fake object with mockk(), tell it what to return using every { ... } returns ..., call your code, then check the fake was used with verify { ... }.

open as a page

What does runTest from kotlinx-coroutines-test do, and why is it needed for deterministic coroutine tests?

level: seniorimportance: must knowfreq 58%

basics

~10 s

runTest runs your suspend code in a special test environment that skips real waiting. Delays are fast-forwarded instead of actually sleeping, so coroutine tests finish instantly and give the same result every run.

open as a page

What is Kotest, and what does its spec-style DSL give you over plain kotlin.test?

level: middleimportance: should knowfreq 50%

basics

~10 s

Kotest is a richer Kotlin testing framework. Instead of @Test methods it lets you describe tests in readable blocks (like 'describe / it'), and it ships many ready-made assertions called matchers.

open as a page

How do you deterministically test a Kotlin Flow, including hot flows like StateFlow that never complete?

level: seniorimportance: should knowfreq 44%

basics

~20 s

For a normal flow you collect its values into a list inside runTest and check them. For a flow that never ends, like StateFlow, you use a tool such as Turbine to read items one at a time and then stop collecting.

open as a page

Map classic test-double theory (stub, mock, fake, spy) onto Kotlin tooling and explain when you'd prefer a fake over a MockK mock.

level: principalimportance: should knowfreq 32%

basics

~20 s

A stub gives canned answers, a mock also checks it was called, a fake is a lightweight working implementation, and a spy wraps a real object. In Kotlin, MockK builds stubs/mocks/spies, while fakes are hand-written classes. Prefer fakes when behavior matters more than call verification.

open as a page