What does fail() return, and how does that Nothing return type let you use it in expressions?
answer
- fail() returns Nothing
- Nothing = bottom type, subtype of all
- usable in ?:, when, if branches
- code after fail is unreachable
- smart-casts the surviving branch to non-null
basics
~20 sfail() always makes the test fail immediately. It returns Nothing, the type with no values, so the compiler treats anything after it as unreachable and lets you use it anywhere a value is expected, like the right side of ?:.
solid answer
~40 s`fail(message: String? = null): Nothing` unconditionally fails the test. Because its return type is `Nothing` — the bottom type that is a subtype of every type and has no instances — the compiler knows control never returns from it. That makes code after `fail()` unreachable and lets `fail()` appear wherever a value is required: as a `when`/`if` branch, the right operand of the elvis operator (`header ?: fail("missing")`), or to satisfy a non-null/typed result. Typical uses: a 'should have thrown' guard in manual try/catch, marking an impossible branch, or a loud TODO stub. For exception assertions specifically, `assertFailsWith<T> { }` is usually cleaner, but that is a sibling topic.
code
kotlin · 9 linesimport kotlin.test.fail
val token: String = header ?: fail("missing auth header")
// token is non-null here because the fail branch can't produce a value
val r = when (state) {
State.OK -> compute()
else -> fail("impossible state")
}go deeper
Knows fail() makes the test fail and can take a message.
Knows fail returns Nothing and that following code is unreachable.
Explains Nothing as the bottom type and uses fail() in expression positions like ?: and when branches.
Reasons about Nothing in the type lattice, exhaustiveness, and when fail() vs assertFailsWith expresses intent most clearly.
## `fail()` — unconditional failure `fail(message: String? = null): Nothing` immediately fails the current test. Its return type is `Nothing`, so the compiler treats code after it as unreachable and it satisfies any expected return type. ```kotlin import kotlin.test.fail val result: Result = when (state) { State.OK -> compute() State.ERROR -> fail("computation entered an impossible state") } ``` ### Common uses - **"Should have thrown" guards** in manual try/catch: ```kotlin try { parse("garbage") fail("expected NumberFormatException") } catch (e: NumberFormatException) { assertEquals("garbage", e.message?.substringAfter(": ")) } ``` (For most cases `assertFailsWith<NumberFormatException> { ... }` is cleaner — but that is a sibling topic.) - Marking an unreachable `else`/`when` branch. - Stubbing a not-yet-implemented test so it fails loudly rather than silently passing (`fail("TODO")`). ### `Nothing` makes it composable Because `fail` returns `Nothing` (the type with no values, a subtype of every type), you can use it as the right-hand side of `?:`, a `when` branch, or anywhere a value is expected: ```kotlin val token = header ?: fail("missing auth header") ``` The variable `token` is non-null afterward because the `fail` branch can never produce a value.
- Why does `val x: Int = cond ?: fail("...")` compile even though fail returns Nothing?Nothing is a subtype of Int (of every type), so it satisfies the expected type; and because it never returns, x can only be the non-fail value.
- When would you use fail() over assertFailsWith?assertFailsWith is preferred for exception tests; fail() suits impossible branches, non-exception 'should not reach here' guards, or TODO stubs.
Nothing is like a trapdoor: any code path that hits it never comes back, so the compiler stops worrying about what type 'should' come out.
saying these in an interview costs you the question
- Says fail returns Unit or Boolean
- Doesn't know what Nothing is
- Thinks code after fail() still runs
- Can't explain why fail works as an elvis right-hand side
- Uses fail+try/catch where assertFailsWith is clearly better