skip to content

Matchers DSL (shouldBe)

Kotest's infix matchers read as sentences, with shouldThrow for failures and assertSoftly to collect several failures in one run. Soft assertions are the detail worth knowing, since they turn three reruns into one informative failure.

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

questions

5

What is Kotest's matcher DSL, and how do you assert that a value equals an expected value using shouldBe and shouldNotBe?

level: juniorimportance: must knowfreq 70%

answer

  1. infix: actual shouldBe expected
  2. shouldNotBe for inequality
  3. uses == / equals (structural)
  4. throws AssertionError with diff
  5. plusOrMinus for doubles

basics

~20 s

Kotest lets you write checks like normal English. You write 'result shouldBe 5' to say result must equal 5, and 'result shouldNotBe 0' to say it must not equal 0. If the check fails, the test fails with a clear message.

solid answer

~40 s

Kotest's matcher DSL provides readable assertions via infix functions. `shouldBe` checks equality and `shouldNotBe` checks inequality: `result shouldBe 5`, `result shouldNotBe 0`. They live in `io.kotest.matchers.shouldBe` / `shouldNotBe` and work on any type using structural equality (`equals`). Because they are infix functions, you write `actual shouldBe expected` with no parentheses. On failure Kotest throws an `AssertionError` with a clean diff-style message (expected vs actual). Equality is exact: for floating point you typically use `result shouldBe (3.14 plusOrMinus 0.01)`. These matchers return the receiver, so they can be chained or combined, and they integrate with any Kotest spec style. They are the most-used building block of the assertion library.

code

kotlin · 9 lines
kotlin
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe

val sum = listOf(1, 2, 3).sum()
sum shouldBe 6
sum shouldNotBe 0

val pi = 22.0 / 7
pi shouldBe (3.14 plusOrMinus 0.01)

go deeper

for a junior

Can write basic shouldBe/shouldNotBe equality checks and knows they fail the test with a clear message.

for a middle

Knows shouldBe uses structural equality, that they're infix functions, and reaches for plusOrMinus on doubles.

for a senior

Explains the matcher abstraction, failure-message diffing, and how the same pattern scales to collection/exception matchers.

for a principal

Frames readable assertions as a team-wide convention and weighs Kotest assertions vs other libraries for maintainability and onboarding.

## What is the matcher DSL? Kotest is a Kotlin testing framework. Its **assertions library** (`kotest-assertions-core`) provides a fluent, readable **Domain-Specific Language (DSL)** for checking values in a test. A *matcher* is an object that decides whether a value satisfies some condition and, if not, produces a failure message. ## `shouldBe` and `shouldNotBe` These are **infix functions** — Kotlin functions declared with the `infix` keyword that you call as `receiver function argument` (no dot, no parentheses). - `actual shouldBe expected` passes when `actual == expected` (structural equality via `equals`). - `actual shouldNotBe unexpected` passes when `actual != unexpected`. ```kotlin import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe val result = 2 + 3 result shouldBe 5 // passes result shouldNotBe 0 // passes "hello".uppercase() shouldBe "HELLO" val user: User? = findUser() user shouldNotBe null ``` ## What happens on failure When the condition is false Kotest throws an `AssertionError` (specifically an assertion failure) with a message that shows **expected** vs **actual**, often as a structured diff for data classes and collections. This is clearer than a bare `assertEquals`. ## Equality details `shouldBe` uses `==` (the `equals` method). So two `data class` instances with equal fields are equal. For numbers, types must match in the usual Kotlin way. For floating point use the approximate matcher: ```kotlin 0.1 + 0.2 shouldBe (0.3 plusOrMinus 0.0001) ``` ## Why infix? The `infix` modifier makes assertions read like sentences, which is the whole point of the DSL. `shouldBe` is the single most common matcher; everything else (collection, string, exception matchers) builds on the same pattern.

  • Why does 'actual shouldBe expected' read better than assertEquals?
    Argument order is natural (subject first), it's infix so no parentheses, and the failure message is a structured expected-vs-actual diff.
  • How do you compare two doubles that may have rounding error?
    Use the tolerance matcher: actual shouldBe (expected plusOrMinus 0.0001).

Like reading the assertion out loud: 'result should be 5' instead of decoding assertEquals(5, result).

saying these in an interview costs you the question

  • Writing the arguments backwards (expected shouldBe actual) and not noticing the message is reversed
  • Using shouldBe to compare doubles for exact equality
  • Thinking shouldBe uses reference identity instead of equals
  • Confusing Kotest's shouldBe with JUnit's assertEquals semantics

context

open as a page

How do you assert that a block of code throws a specific exception in Kotest, and what is the difference between shouldThrow<T> and shouldThrowExactly<T>?

level: middleimportance: must knowfreq 65%

basics

~10 s

Wrap the risky code in shouldThrow<SomeException> { ... }. The test passes only if that code throws that exception type. It also hands you the caught exception so you can check its message.

open as a page

Which Kotest matchers would you use to assert membership, size, and ordering on collections and substrings on strings? Give concrete examples.

level: middleimportance: should knowfreq 55%

basics

~10 s

For lists use matchers like shouldContain (has an element), shouldHaveSize (length), shouldBeEmpty, and shouldContainExactly (same elements in order). For strings use shouldContain, shouldStartWith, shouldEndWith, and shouldMatch for a regex.

open as a page

What problem does assertSoftly solve in Kotest, and how does it change the behavior of multiple matcher assertions inside its block?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Normally a test stops at the first failed check, hiding the rest. assertSoftly runs all the checks in its block, collects every failure, and reports them together at the end, so you see all problems in one run.

open as a page

How do you write a reusable custom matcher in Kotest, and how do you compose matchers with and/or and negation?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

You create a small object implementing Kotest's Matcher interface, returning whether the value passed plus messages for failure and for the negated case. Then you can combine matchers with .and() / .or() and flip them with .invert().

open as a page