A JUnit 5 test class extends an abstract base class, and both declare @BeforeEach and @AfterEach methods. In what order do the four run, and what changes if the subclass overrides the base class's hook method?
answer
- before: superclass → subclass
- after: subclass → superclass
- override ⇒ only the override runs
- annotations are not inherited onto overrides
- sibling hooks in one class: order unspecified
basics
~20 sSetup runs top-down: the superclass @BeforeEach first, then the subclass's. Teardown mirrors it bottom-up: the subclass @AfterEach first, then the superclass's. If the subclass overrides the inherited hook method, only the override runs — the superclass version is not executed separately.
solid answer
~50 sJupiter runs inherited lifecycle methods so that setup unwinds like a stack: - `@BeforeEach`: **superclass before subclass** (outermost first). - `@AfterEach`: **subclass before superclass** (the reverse). The same top-down/bottom-up rule applies to `@BeforeAll`/`@AfterAll`. The logic is that the subclass's setup may depend on the base's, so the base must be ready first and torn down last. Overriding changes things: if the subclass declares a method that **overrides** the superclass's hook — same name and signature — only the overriding method is executed, once. If the override is not itself annotated, the hook does not run at all, because Java does not inherit method annotations onto overrides in a way that would re-trigger the shadowed method. That silent disappearance is a classic bug. Within a single class, multiple methods carrying the same annotation are all run, but in an order Jupiter deliberately leaves unspecified — never depend on it.
code
java · 21 linesimport org.junit.jupiter.api.*;
abstract class BaseTest {
@BeforeEach void openConnection() { System.out.println("1 base before"); }
@AfterEach void closeConnection() { System.out.println("4 base after"); }
}
class ChildTest extends BaseTest {
@BeforeEach void seedData() { System.out.println("2 child before"); }
@AfterEach void wipeData() { System.out.println("3 child after"); }
@Test void works() { /* prints 1,2,3,4 around this */ }
}
class BrokenChildTest extends BaseTest {
// Overrides the base hook WITHOUT re-annotating:
// openConnection() now never runs as a lifecycle hook.
@Override void openConnection() { }
@Test void silentlyMissesSetup() { }
}go deeper
Remember the two directions: superclass first for setup, subclass first for teardown.
Explain the dependency reasoning behind the mirrored order and know that an override replaces rather than adds to the base hook.
Highlight the unannotated-override trap and the unspecified intra-class ordering, and steer teams towards extensions instead of deep base-class hierarchies.
Treat fixture reuse as an architecture decision: extensions compose and are visible at the use site, whereas inheritance consumes the single-superclass slot and hides setup from the reader.
## The ordering rule When lifecycle methods are inherited from a superclass, JUnit Jupiter orders them so the class hierarchy behaves like nested scopes: ``` @BeforeAll (superclass) @BeforeAll (subclass) @BeforeEach (superclass) @BeforeEach (subclass) test method @AfterEach (subclass) @AfterEach (superclass) @AfterAll (subclass) @AfterAll (superclass) ``` Setup proceeds from the most general to the most specific; teardown proceeds in the exact reverse. The rationale is dependency direction: a subclass fixture is typically built on top of the base fixture (the base starts a server, the subclass registers a route on it), so the base must be initialised first and released last. Releasing the base first would pull the ground out from under the subclass's teardown. This mirrors constructor/`finally` semantics in Java generally, which is a useful way to remember it. ## Overriding and hiding The subtlety candidates get wrong is what happens when the subclass declares a method with the same signature as an inherited hook. **Instance methods — overriding.** If `Sub.setUp()` overrides `Base.setUp()`, there is only one method in the virtual dispatch table for that signature. Jupiter executes the override once; the superclass body does **not** run separately. If the subclass still wants the base behaviour it must call `super.setUp()` explicitly. **The annotation trap.** Java does not inherit annotations on methods. If `Base.setUp()` is annotated `@BeforeEach` and `Sub` overrides it *without* re-annotating, the hook is not discovered on the subclass and the setup silently stops happening — the tests may still pass for a while and then fail mysteriously when the missing initialisation starts to matter. Always re-annotate the override (or, better, do not override lifecycle methods at all: give the subclass its own differently-named `@BeforeEach`, which then runs *in addition to* the base one, in the documented order). **Static methods — hiding.** A static method in the subclass with the same signature as a static `@BeforeAll` in the superclass *hides* rather than overrides it, with the same practical outcome: the hidden superclass method is not executed. **Private methods.** Lifecycle methods must not be private; a private method in the base class is not inherited at all, so it could never participate in a subclass's lifecycle. ## Multiple hooks in one class A class may declare several `@BeforeEach` methods; Jupiter runs all of them, but the relative order among them is deterministic for a given run yet intentionally unspecified — it is derived from reflection and is not source order. Do not build dependencies between sibling hooks. If two setup steps must be sequenced, put them in one method or have one call the other. The inheritance ordering above is guaranteed; the intra-class ordering is not. ## Interfaces Lifecycle methods can also be inherited from interface `default` methods, which is the mechanism behind reusable "test trait" interfaces. Interface-declared hooks run before those declared in the class for `@BeforeEach`, following the same outer-to-inner principle. ## Practical guidance - Prefer **composition over inheritance** for shared fixtures. A JUnit extension registered with `@ExtendWith`, or a helper object, avoids deep hook hierarchies entirely and makes the setup explicit at the point of use. - If you do use a base class, give hooks **distinct names** (`setUpBase`, `setUpRepository`) so nobody accidentally overrides one. - Make base-class hooks `final` where the language allows, to prevent accidental overriding. - Keep hierarchies shallow. Three levels of `@BeforeEach` is a strong signal that the fixture wants to be an extension or a builder. ## Answering well State the two directions crisply (superclass-first for before, subclass-first for after), explain the dependency reasoning, then bring up the override trap — the annotation not being inherited — because that is the part that actually bites teams, and the part that separates someone who has debugged a real hierarchy from someone who has read the table.
- Your team has three levels of test base classes each contributing @BeforeEach setup. What would you change?Replace the inheritance with composition: move each fixture concern into a JUnit extension registered with @ExtendWith or @RegisterExtension, or into a plain helper object constructed in the leaf class's @BeforeEach. Extensions compose without a single-inheritance constraint, make the participating fixtures visible at the class that uses them, and remove the risk of accidental overrides. Deep hook hierarchies also make failures hard to attribute because the stack of setup is invisible at the test.
- If a class declares two @BeforeEach methods, which runs first?Both run, but JUnit deliberately does not specify their relative order — it is deterministic for a given run yet derived from reflection rather than source order, so it must not be relied on. If one setup step depends on another, merge them into a single method or have one call the other. Only the superclass-to-subclass ordering across the hierarchy is guaranteed.
Getting dressed: coat goes on last and comes off first; the base layer goes on first and comes off last. The superclass is the base layer.
saying these in an interview costs you the question
- Saying subclass @BeforeEach runs before the superclass's
- Expecting teardown to follow the same top-down order as setup
- Believing an overridden superclass hook still runs in addition to the override
- Assuming a method annotation is inherited by an unannotated override
- Relying on declaration order for multiple @BeforeEach methods in one class