What is the difference between assertFails and assertFailsWith in kotlin.test, and when would you choose each?
answer
- assertFails = any Throwable, returns Throwable
- assertFailsWith<T> = specific type, returns T
- Specific type guards against masking bugs
- assertFails has a message overload
- Default to assertFailsWith
basics
~10 sassertFails 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
Knows assertFails ignores type while assertFailsWith pins a type.
Explains both return the throwable, the typing difference, and the message overload on assertFails.
Argues for assertFailsWith as the default because loose type assertions mask regressions, and reasons about when assertFails is legitimate.
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