skip to content

How do you use @Nested in Kotlin to group JUnit 5 tests, and what lifecycle behaviour applies to nested classes?

level: middleimportance: should knowfreq 50%

answer

  1. @Nested groups tests into a tree
  2. Must be `inner class` in Kotlin (non-static)
  3. Outer @BeforeEach runs before inner @BeforeEach
  4. inner gives access to enclosing instance state
  5. Nested @BeforeAll still needs PER_CLASS

basics

~10 s

You put related tests inside an inner class marked @Nested. JUnit runs them as a sub-group, and the inner class can access the outer class's fields, which helps share setup.

solid answer

~40 s

`@org.junit.jupiter.api.Nested` marks a member class whose `@Test` methods form a logical sub-group in the report (rendered as a tree). In Kotlin the nested class must be a non-static **`inner class`**, because JUnit requires a non-static nested class so the inner instance can reach the enclosing instance and its setup. Each `@Nested` class has its own `@BeforeEach`/`@AfterEach`, and outer `@BeforeEach` runs *before* inner ones (outer-to-inner), so you can layer context. `@BeforeAll` in a nested class normally requires PER_CLASS just like at top level. A common pattern: an outer `when … (given a logged-in user)` group with several inner `@Nested` scenario classes. This reads like a BDD spec and avoids repeating shared arrange code. Without `inner`, the test class is static and JUnit will not instantiate it correctly for nesting.

code

kotlin · 14 lines
kotlin
import org.junit.jupiter.api.*
import kotlin.test.assertEquals

class CalculatorTest {
    private val calc = Calculator()

    @Nested
    inner class Division {
        @Test fun `divides evenly`() { assertEquals(2, calc.div(4, 2)) }
        @Test fun `throws on zero`() {
            assertThrows<ArithmeticException> { calc.div(1, 0) }
        }
    }
}

go deeper

for a junior

Knows @Nested groups related tests and reports them as a tree.

for a middle

Knows the Kotlin inner requirement and that nested classes get their own @BeforeEach.

for a senior

Explains outer-to-inner lifecycle layering and the PER_CLASS need for nested @BeforeAll.

for a principal

Guides BDD-style grouping conventions and warns against over-nesting for maintainability.

## What @Nested does `@Nested` (from `org.junit.jupiter.api.Nested`) turns a member class into a **grouped sub-suite**. The IDE and reports render it as a tree: the outer class name, then each nested class, then its tests. This expresses hierarchy — e.g. shared context with several scenarios. ## Kotlin requirement: `inner class` JUnit requires nested test classes to be **non-static**. In Kotlin, a nested class is *static by default*; you must add the **`inner`** keyword to make it hold a reference to the enclosing instance: ```kotlin import org.junit.jupiter.api.* class StackTest { private val stack = ArrayDeque<Int>() @Test fun `is empty initially`() { assert(stack.isEmpty()) } @Nested inner class AfterPushing { @BeforeEach fun push() { stack.addLast(42) } // sees outer 'stack' @Test fun `is not empty`() { assert(stack.isNotEmpty()) } @Test fun `pop returns the value`() { assertEquals(42, stack.removeLast()) } } } ``` Without `inner`, the class is static, cannot access `stack`, and JUnit will not run it as a nested test. ## Lifecycle layering (outer-to-inner) For each test inside a `@Nested` class: 1. The **outer** class's `@BeforeEach` runs first. 2. Then the **nested** class's `@BeforeEach`. 3. The test runs. 4. Nested `@AfterEach`, then outer `@AfterEach`. This lets you build context in layers (outer = base arrange, inner = scenario tweak). ## @BeforeAll in nested classes A nested class can have `@BeforeAll`, but — as everywhere — it must be static **or** the nested class must carry `@TestInstance(TestInstance.Lifecycle.PER_CLASS)`. PER_CLASS can be inherited by nested classes from the outer class default. ## When to use it - BDD-style `given/when/then` grouping. - Sharing arrange code across several related scenarios. - Producing a readable tree report instead of a flat list. Keep nesting shallow (one or two levels); deep trees hurt readability.

  • What happens if you forget the `inner` keyword on a @Nested class in Kotlin?
    The class is static, cannot access enclosing-instance state, and JUnit will not execute it as a nested test (or errors).
  • In what order do outer and inner @BeforeEach run?
    Outer-to-inner: the enclosing class's @BeforeEach runs first, then the nested class's, then the test.

@Nested is like chapters and sub-sections in a spec document, where each sub-section inherits the chapter's setup.

saying these in an interview costs you the question

  • Forgetting that Kotlin nested classes are static unless marked `inner`
  • Saying inner @BeforeEach runs before outer @BeforeEach
  • Claiming @Nested classes can't have their own lifecycle methods
  • Deeply nesting many levels and harming readability

context