Why must a @Nested class be non-static, and how does that affect access to the outer test instance's state?
answer
- Non-static inner class ⇒ holds Outer.this reference
- Static nested ⇒ no enclosing instance, breaks sharing
- Default lifecycle PER_METHOD: fresh outer per test method
- Whole enclosing chain instantiated per nested test
- PER_CLASS reuses instance, can leak mutable state
basics
~20 sA @Nested class must be non-static so it holds a reference to an instance of the outer test class. That lets nested tests read and reuse the outer class's fields and setup. A static nested class would be independent and couldn't share that instance state.
solid answer
~50 sJUnit's default test instance lifecycle is PER_METHOD: a fresh outer instance is created for each test method. A @Nested class is a non-static inner class, so each of its instances is bound to an instance of the enclosing class and can access the outer instance's fields and helpers. JUnit instantiates the full enclosing chain for every nested test, so outer @BeforeEach setup and shared fields are available to nested tests — that's the whole point of nesting: arrange common state in the parent, refine it in the child. If the class were static, it would be an independent nested class with no implicit outer instance, breaking the shared-context model JUnit depends on; JUnit therefore requires @Nested classes to be non-static. Note the outer instance is fresh per test method (unless you opt into @TestInstance(PER_CLASS)), so nested tests don't accidentally leak mutable state between methods.
code
java · 17 linesclass OrderTest {
Order order;
@BeforeEach
void base() { order = new Order(); }
@Nested
class WhenPaid {
@BeforeEach
void pay() { order.markPaid(); } // shares outer 'order'
@Test
void isComplete() {
assertTrue(order.isComplete());
}
}
}go deeper
Knows the nested class must be non-static and that this lets it use the outer class's fields.
Explains the Outer.this reference, why static breaks sharing, and that PER_METHOD gives a fresh outer instance per test method.
Reasons about lifecycle (PER_METHOD vs PER_CLASS) consequences for shared mutable state and designs nested setup to avoid coupling between methods.
Guides the team on lifecycle conventions and isolation guarantees so large nested suites stay deterministic and free of cross-test state leakage.
## The Java mechanism behind the rule In Java, a class declared inside another class is **nested**. There are two kinds: - A **static nested class** (declared `static`) is essentially a top-level class that happens to live inside another for namespacing. It has *no* link to any instance of the enclosing class. - A **non-static inner class** (no `static` keyword) is tied to an **instance** of the enclosing class. Every inner-class object carries a hidden reference to an *outer-class object* (you can write it explicitly as `Outer.this`). Through that reference, inner-class code can read the outer object's **fields** and call its methods. `@Nested` requires the **non-static** form precisely because JUnit wants the nested tests to *share* the enclosing test's context — its fields and its setup. ## Test instance lifecycle JUnit 5 creates **test instances** to run methods on. By default the lifecycle is **`PER_METHOD`**: for *every* `@Test` method, JUnit builds a brand-new instance of the test class. When that test lives in a `@Nested` class, JUnit builds the **entire enclosing chain** — an instance of the outer class *and* an instance of the nested class linked to it — for that one test method. Because the nested instance is linked to a fresh outer instance, any field on the outer class, and any `@BeforeEach` it ran, is visible to the nested test: ```java class OrderTest { Order order; // outer field @BeforeEach void base() { order = new Order(); } // outer setup runs first @Nested class WhenPaid { @BeforeEach void pay() { order.markPaid(); } // reads the SAME outer 'order' @Test void isComplete() { assertTrue(order.isComplete()); } } } ``` Here the nested `WhenPaid.pay()` mutates the `order` field that the outer `base()` created — they share the same outer instance for that test. ## Why static breaks it If you wrote `static class WhenPaid`, the class would have **no enclosing instance**. It couldn't reference the non-static `order` field at all (it wouldn't compile), and JUnit would not run the outer instance setup as part of its lifecycle. The shared-context model collapses. So JUnit's contract is: **@Nested test classes must be non-static inner classes.** (Top-level test classes are the opposite — they're effectively standalone — but nested *grouping* classes are inner.) ## PER_METHOD vs PER_CLASS With the default `PER_METHOD`, state does **not** leak between test methods because each gets a fresh outer+nested instance pair. If you annotate a class with **`@TestInstance(TestInstance.Lifecycle.PER_CLASS)`**, JUnit reuses one instance across all that class's methods (which also enables non-static `@BeforeAll`). That choice changes whether mutable fields persist across methods — relevant when reasoning about shared state in nested groups, and a source of accidental coupling if you mutate shared fields and rely on `PER_CLASS`. ## Summary Non-static is mandatory so the nested test inherits an *instance* of the enclosing test, giving it access to outer fields and setup. That shared, per-method-fresh context is exactly what makes `@Nested` useful for layering arrangement from general (parent) to specific (child).
- What happens if you mark a @Nested class static?It loses the enclosing instance link, can't access non-static outer fields (compile error if it tries), and JUnit won't treat it as a context-sharing nested group. JUnit requires non-static.
- Does outer-instance state leak between nested test methods by default?No. The default PER_METHOD lifecycle builds a fresh outer+nested instance pair per test method, so mutable fields are reset. Only PER_CLASS would reuse an instance and risk leakage.
saying these in an interview costs you the question
- Claiming nested tests share one outer instance across all methods by default (they don't — PER_METHOD is fresh per method).
- Saying a static class works the same as @Nested.
- Forgetting that a static nested class can't access non-static outer fields at all.