You add a @BeforeAll method to a class annotated @Nested in JUnit 5 and the run fails saying the method must be static — but making it static does not compile. What causes this, and what are your options for one-time setup scoped to that nested class?
answer
- @BeforeAll wants static; inner class forbids static (pre-Java 16)
- @TestInstance(PER_CLASS) on the nested class
- Java 16+ and JUnit 5.9+ allow static in @Nested
- hoist to outer class @BeforeAll
- or an extension with a scoped store
basics
~20 s@BeforeAll must be static by default, but a @Nested class is a non-static inner class, which historically cannot declare static members — hence the deadlock. Fix it by annotating the nested class @TestInstance(PER_CLASS), which allows a non-static @BeforeAll; or move the setup to the outer class, or to @BeforeEach, or to an extension.
solid answer
~60 sTwo rules collide. `@BeforeAll` is a class-level hook, so under the default per-method lifecycle it must be `static`. A `@Nested` class must be a **non-static inner class**, and inner classes could not declare static members before Java 16 — so `static` does not compile and non-static is rejected. Options, best first: 1. **Annotate the nested class `@TestInstance(TestInstance.Lifecycle.PER_CLASS)`.** One instance serves the nested class, so `@BeforeAll` may be an instance method. This is the idiomatic answer. 2. **On Java 16+ with JUnit 5.9 or newer**, a `static` `@BeforeAll` inside a `@Nested` class is permitted, since the language now allows static members in inner classes. 3. **Hoist the setup** to the outer class's `@BeforeAll` if it is really shared by the whole tree. 4. **Use `@BeforeEach`** if the work is cheap — often it was never one-time work to begin with. 5. **Move it into an extension** with a scoped store when the resource is expensive and shared more broadly. Remember option 1 buys shared mutable state inside that nested class.
code
java · 24 linesclass AuthTest {
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class WhenTokenExpired {
private FakeAuthServer server; // immutable after setup
@BeforeAll
void startServer() { // no 'static' required
server = FakeAuthServer.start();
}
@AfterAll
void stopServer() {
server.close();
}
@Test
void rejectsExpiredToken() {
assertEquals(401, server.call("/me", expiredToken()));
}
}
}go deeper
Know the symptom and the standard fix: add @TestInstance(PER_CLASS) to the nested class so @BeforeAll can be non-static.
Explain both constraints — class-level hooks want static, @Nested requires a non-static inner class — and name the Java 16 / JUnit 5.9 alternative.
Weigh the options: hoist to the outer class, use @BeforeEach if the cost is small, or PER_CLASS with the shared-state caveat spelled out.
Push the question upstream — decide where the resource's lifetime really belongs (class, tree, whole run) and prefer an extension with a scoped store when it is shared beyond one class.
## The collision, precisely Two independent constraints meet here. **Constraint A — `@BeforeAll` is class-scoped.** It runs once for a test class, before any instance exists under Jupiter's default `PER_METHOD` lifecycle. With no instance to invoke it on, the method must be `static`, and Jupiter raises a configuration exception if it is not. **Constraint B — `@Nested` requires a non-static inner class.** JUnit binds the nested test instance to an enclosing instance, which is what lets nested tests use the outer fixture; a `static` nested class has no enclosing instance and is not a nested test class in that sense. And the Java language rule that closes the trap: **before Java 16, an inner (non-static) class could not declare static members**, so writing `static void setUpAll()` inside a `@Nested` class did not even compile. Annotation says "must be static", compiler says "cannot be static". That is the error people hit. ## Option 1 — @TestInstance(PER_CLASS) on the nested class (the idiomatic fix) ```java @Nested @TestInstance(TestInstance.Lifecycle.PER_CLASS) class WhenTokenExpired { private Server server; @BeforeAll void startServer() { server = Server.start(); } @AfterAll void stopServer() { server.close(); } } ``` With `PER_CLASS`, Jupiter creates a **single instance of the nested class** for all its test methods, so it can invoke a non-static `@BeforeAll` on that instance. The hook still runs exactly once, before the nested class's first test. Note the lifecycle annotation applies to the class it is on. Putting it on the nested class does not change the outer class, and putting it on the outer class does not automatically give a nested class per-class instances — each declares its own (subject to the configured default). The cost is the usual `PER_CLASS` cost: the nested class's instance fields now persist across its test methods, so mutable state can leak between them and parallel methods share one object. Keep the shared fixture immutable after `@BeforeAll` and reset anything mutable in `@BeforeEach`. ## Option 2 — Java 16+ and JUnit 5.9+ Java 16 lifted the restriction on static members in inner classes, and JUnit Jupiter 5.9 added support for `static` `@BeforeAll`/`@AfterAll` declared in `@Nested` classes when the language level allows it. If your toolchain qualifies, a plain `static` hook works with no lifecycle change and no shared-state cost: ```java @Nested class WhenTokenExpired { @BeforeAll static void startServer() { /* ... */ } } ``` Mention both options in an interview; naming the version gate is the detail that signals real experience. On an older toolchain, option 1 is the only in-class route. ## Option 3 — hoist it to the outer class If the setup is genuinely shared by every scenario in the tree, it belongs to the outer class, where a plain `static @BeforeAll` is legal because the outer class is top-level. It then runs once before any test in the outer class or in any nested class. This is often the right answer: people put setup in the nested class out of habit, not because the scope demands it. ## Option 4 — reconsider whether it must be one-time A surprising share of "one-time" setup is a few milliseconds of object construction that would be clearer, safer and barely slower in `@BeforeEach`, with full per-test isolation preserved. Ask what the actual cost is before trading isolation for it. ## Option 5 — move it into an extension When the resource is expensive and used by more than this nested class — a container, an embedded broker, a compiled artifact — an extension implementing `BeforeAllCallback` and storing the resource in an `ExtensionContext.Store` at a suitable scope handles creation once and closes it when the scope ends. This scales better than per-class hooks: the extension is registered once and reused across classes, and cleanup is tied to the store's lifecycle rather than to a hand-written `@AfterAll`. ## Symmetry with @AfterAll Everything above applies identically to `@AfterAll`. And note that `@AfterAll` in a nested `PER_CLASS` class runs after that nested class's last test, while the **outer** class's `@AfterAll` runs after the entire tree — so ordering of teardown across levels is inner-first, matching the general inside-out unwinding rule. ## The answer shape that scores Name both constraints, name the Java-16/JUnit-5.9 gate, give `PER_CLASS` as the portable fix, and add the caveat that it introduces shared state inside the nested class. Then finish with the design point: check whether the setup should have been in the outer class or an extension in the first place.
- Does putting @TestInstance(PER_CLASS) on the outer class also give the nested classes per-class instances?No. The lifecycle applies to the class it is declared on. Placing it on the outer class makes one outer instance serve the whole tree, but each nested class still resolves its own lifecycle — from its own annotation if present, otherwise from the configured default. To get a non-static @BeforeAll inside a nested class you annotate that nested class.
- What do you give up by fixing this with PER_CLASS instead of hoisting the setup?Per-test isolation inside that nested class. One instance now serves all its test methods, so its fields persist between them and can carry state forward, and concurrent methods share the object. The mitigation is to treat the shared fixture as read-only after @BeforeAll and to keep anything a test mutates in @BeforeEach or as a local variable.
- When would you push this setup into a JUnit extension instead?When the expensive resource is used by more than one test class, or when its lifetime should outlive a single class — a container, an embedded broker, a compiled schema. An extension implementing BeforeAllCallback can create it once and keep it in an ExtensionContext.Store at the right scope, with cleanup tied to that store closing. That removes hand-written @AfterAll teardown and lets many classes share one instance without any lifecycle tricks.
saying these in an interview costs you the question
- Making the nested class static to allow a static @BeforeAll, which stops it being a nested test class bound to the outer instance
- Claiming @BeforeAll simply cannot exist inside a @Nested class under any configuration
- Thinking PER_CLASS on the outer class propagates the lifecycle to nested classes
- Believing PER_CLASS changes when @BeforeAll runs rather than only allowing it to be non-static
- Ignoring that the PER_CLASS fix introduces shared mutable state within the nested class