skip to content

What is the difference between assertFails and assertFailsWith in kotlin.test, and when would you choose each?

level: middleimportance: should knowfreq 55%

answer

  1. assertFails = any Throwable, returns Throwable
  2. assertFailsWith<T> = specific type, returns T
  3. Specific type guards against masking bugs
  4. assertFails has a message overload
  5. Default to assertFailsWith

basics

~10 s

assertFails only checks that something was thrown, ignoring the type, and returns the Throwable. assertFailsWith checks for a specific type. Use assertFails when any failure is fine; use assertFailsWith when the exact exception matters.

solid answer

~40 s

`assertFails { block }` asserts the block throws *any* Throwable and returns it as `Throwable`. `assertFailsWith<T> { block }` asserts it throws `T` (or a subtype) and returns it typed as `T`. Prefer `assertFailsWith` in almost all real tests because asserting the precise type prevents a test from passing when an unrelated bug throws a different exception (for example, a `NullPointerException` masking a missing validation). `assertFails` is acceptable only when the type genuinely should not be pinned — e.g. a quick smoke check or a platform whose exception type is unstable. Both return the caught throwable for further assertions; `assertFails` gives you the broader `Throwable` type so you must cast to inspect type-specific members. There is also an overloaded `assertFails(message) { }` taking a custom failure message.

go deeper

for a junior

Knows assertFails ignores type while assertFailsWith pins a type.

for a middle

Explains both return the throwable, the typing difference, and the message overload on assertFails.

for a senior

Argues for assertFailsWith as the default because loose type assertions mask regressions, and reasons about when assertFails is legitimate.

for a principal

Sets team testing conventions around failure-path precision and weighs platform-specific exception stability when choosing.

## Two failure assertions Both live in **`kotlin.test`** and both run a lambda expecting it to throw. ### `assertFails` ```kotlin val t: Throwable = assertFails { riskyCall() } ``` - Passes if the block throws **any** `Throwable`. - Returns the caught throwable typed as **`Throwable`**. - An overload `assertFails(message: String?) { ... }` lets you attach a custom message shown on failure. ### `assertFailsWith` ```kotlin val e: IllegalStateException = assertFailsWith<IllegalStateException> { riskyCall() } ``` - Passes only if the block throws **`T` or a subtype**. - Returns the throwable typed as **`T`**, so type-specific members are directly accessible. ## Why type-specificity matters Consider testing that input validation rejects bad data: ```kotlin // Weak: passes even if a NullPointerException sneaks in assertFails { service.register(null) } // Strong: only passes for the validation exception you expect assertFailsWith<IllegalArgumentException> { service.register(null) } ``` The weak version would still go green if a refactor introduced an unrelated NPE — hiding a real bug. The strong version pins the contract. ## Inspecting the result Because `assertFails` returns `Throwable`, checking a subtype-specific property requires a cast or smart-cast: ```kotlin val t = assertFails { parse(bad) } assertTrue(t is NumberFormatException) ``` With `assertFailsWith<NumberFormatException>` the return is already typed, no cast needed. ## Decision guide - **Default:** `assertFailsWith<T>` — pins the exact contract. - **Type intentionally unconstrained / smoke test:** `assertFails`. - Both: capture the return value to assert on `message`/`cause`.

  • What concrete risk does using assertFails everywhere introduce?
    A test can pass on the wrong exception (e.g. an unrelated NPE), so it no longer verifies the intended failure contract and can hide regressions.
  • What is the return type of assertFails versus assertFailsWith<T>?
    assertFails returns Throwable; assertFailsWith<T> returns T, avoiding a cast when inspecting type-specific members.

saying these in an interview costs you the question

  • Claiming assertFails checks the type (it does not)
  • Saying assertFails returns nothing useful
  • Defaulting to assertFails for normal validation tests
  • Not realizing assertFailsWith accepts subtypes

context