skip to content

When a JUnit 5 test class extends a base test class, in what order do the @BeforeEach and @AfterEach methods of the superclass and subclass execute?

level: seniorimportance: should knowfreq 40%

answer

  1. Onion: super BeforeEach → sub BeforeEach → test → sub AfterEach → super AfterEach
  2. Before = parent-first (like constructor chaining)
  3. After = child-first (reverse unwind)
  4. Same-class multiple @BeforeEach order is undefined
  5. Override with same signature replaces (not runs twice)

basics

~10 s

Superclass @BeforeEach methods run before subclass @BeforeEach methods, and subclass @AfterEach methods run before superclass @AfterEach methods. It nests like constructors: setup goes top-down (parent first), cleanup goes bottom-up (child first).

solid answer

~50 s

JUnit 5 treats inherited lifecycle methods as a nested onion. For setup, it walks the class hierarchy top-down: a superclass's @BeforeEach runs before the subclass's @BeforeEach, mirroring constructor chaining where the parent initializes first. For teardown it reverses: the subclass's @AfterEach runs before the superclass's @AfterEach, so the most-derived cleanup happens first and the base cleans up last. Within a single class, multiple @BeforeEach (or @AfterEach) methods all run but their relative order is not guaranteed — you can pin it with @Order plus @TestMethodOrder-style ordering only via the MethodOrderer for tests, not lifecycle, so don't depend on it. Inherited methods that are not overridden are collected and run; an overriding method with the same signature is not run twice. This lets a base test class establish shared scaffolding (mocks, context) that every subclass inherits without redeclaration.

code

java · 20 lines
java
import org.junit.jupiter.api.*;

class BaseTest {
    @BeforeEach void baseSetup()   { System.out.println("1 base @BeforeEach"); }
    @AfterEach  void baseCleanup() { System.out.println("4 base @AfterEach"); }
}

class ChildTest extends BaseTest {
    @BeforeEach void childSetup()   { System.out.println("2 child @BeforeEach"); }
    @AfterEach  void childCleanup() { System.out.println("3 child @AfterEach"); }

    @Test void runs() { System.out.println("-- test body --"); }
}

// Output for ChildTest.runs():
// 1 base @BeforeEach
// 2 child @BeforeEach
// -- test body --
// 3 child @AfterEach
// 4 base @AfterEach

go deeper

for a junior

Knows base-class setup runs before subclass setup; may not recall the reversed teardown order.

for a middle

States the super-before-sub / sub-before-super rule and links it to constructor-chaining intuition.

for a senior

Explains the onion nesting, undefined intra-class ordering, override-vs-declare pitfalls, and how to design base test classes safely.

for a principal

Establishes team conventions for base-test hierarchies and @Nested structuring, avoids order-coupling, and reasons about how inherited fixtures interact with parallel execution and shared state.

## Why ordering matters It's common to put shared test setup in a **base test class** — e.g. an `AbstractServiceTest` that creates mocks or boots a Spring context — and let concrete test classes extend it. To reason about such a hierarchy you must know *in what order* JUnit 5 runs the inherited `@BeforeEach`/`@AfterEach` methods, because the subclass's setup often depends on the superclass's having already run. ## The onion model JUnit 5 runs lifecycle callbacks like nested layers of an onion, **symmetric around the test body**: ``` [super @BeforeEach] [sub @BeforeEach] ( @Test body ) [sub @AfterEach] [super @AfterEach] ``` - **`@BeforeEach`: superclass first, then subclass** (top-down / outer-to-inner). This mirrors **constructor chaining** in Java, where a subclass constructor implicitly calls `super(...)` first, so the parent's part of the object is initialized before the child's. The base test sets up the broad context; the subclass refines it. - **`@AfterEach`: subclass first, then superclass** (bottom-up / inner-to-outer). Cleanup unwinds in reverse so a subclass can tear down what it added before the base tears down the foundation it sits on — exactly like properly nested resource release. The same nesting applies to `@BeforeAll` (super before sub) and `@AfterAll` (sub before super), and also to **`@Nested`** inner test classes: the enclosing class's `@BeforeEach` runs before the nested class's, and the enclosing `@AfterEach` runs after. ## Ordering *within* one class is undefined If a single class declares **several** `@BeforeEach` methods, JUnit runs **all** of them, but **does not guarantee their relative order**. JUnit deliberately doesn't promise a deterministic order for multiple same-phase lifecycle methods in one class (it derives from reflection order, which you must not rely on). The robust design is **one** `@BeforeEach` per class, or methods whose effects are order-independent. (`@Order`/`MethodOrderer` control *test method* execution order, not lifecycle-method order.) ## Overriding vs declaring Lifecycle methods are ordinary inherited methods. If a subclass **overrides** a superclass `@BeforeEach` (same signature), only the overriding version runs — it is *not* invoked twice. If you intend the base setup to also run, the override should call `super.theSetupMethod()` explicitly, or you should give the methods *different names* so both are collected and run (super first). A subtle bug: naming a subclass setup method identically to the base's accidentally hides/overrides it and silently drops the base setup. ## Practical consequences - Put cross-cutting setup (mock framework init, shared fixtures, transaction begin) in a base class's `@BeforeEach`; it runs first for every subclass automatically. - Make subclass setup depend only on what the base guarantees has already run. - For teardown that must release resources the base created, remember the base's `@AfterEach` runs *last*, so the base can safely close what it opened after subclasses finish. - Don't bury order-sensitive logic across multiple same-class lifecycle methods. ## Summary table | Phase | Order | |---|---| | `@BeforeAll` | super → sub (static, once) | | `@BeforeEach` | super → sub (each test) | | `@Test` | the test body | | `@AfterEach` | sub → super (each test) | | `@AfterAll` | sub → super (static, once) | The rule to memorize: **'Before' goes parent-first; 'After' goes child-first** — setup builds up, teardown unwinds.

  • What happens if a subclass declares a @BeforeEach method with the same name and signature as the base class's?
    It overrides (hides) the base method, so only the subclass version runs — the base setup is silently dropped unless the subclass calls super explicitly. Use distinct method names so JUnit collects and runs both (super first).
  • Does the same super-before-sub ordering apply to @Nested test classes?
    Yes. For @Nested classes, the enclosing class's @BeforeEach runs before the nested class's @BeforeEach, and the enclosing @AfterEach runs after the nested one — the same outer-to-inner setup, inner-to-outer teardown nesting as inheritance.

saying these in an interview costs you the question

  • Claiming subclass @BeforeEach runs before the superclass's
  • Assuming a deterministic order for multiple @BeforeEach in one class
  • Thinking an overriding setup method still also runs the base version automatically
  • Saying @AfterEach order matches @BeforeEach order (it reverses)

context