skip to content

Lifecycle & Fixtures

When test instances are created and hooks fire — PER_METHOD versus PER_CLASS, @BeforeAll and @AfterEach ordering, and controlling test order. Asked because shared mutable state across tests is a classic source of flakiness.

on this pageshow

explore

questions

18

In JUnit 5, describe when the methods annotated @BeforeAll, @BeforeEach, @AfterEach and @AfterAll run relative to the test methods of a class, and what each is typically used for.

level: juniorimportance: must knowfreq 72%

answer

  1. BeforeAll once → (BeforeEach, test, AfterEach) × N → AfterAll once
  2. mutable ⇒ @BeforeEach
  3. expensive + immutable ⇒ @BeforeAll
  4. teardown runs even after failure
  5. JUnit 4 names: BeforeClass/Before/After/AfterClass

basics

~20 s

@BeforeAll runs once before any test in the class; @AfterAll runs once after the last one. @BeforeEach runs before every individual test and @AfterEach after every one, so for three tests you get one BeforeAll, three BeforeEach/test/AfterEach cycles, then one AfterAll.

solid answer

~40 s

For a class with N test methods the order is: 1. `@BeforeAll` — once, before any test. 2. For each test: `@BeforeEach` → the `@Test` method → `@AfterEach`. 3. `@AfterAll` — once, after the last test. So three tests give one `@BeforeAll`, three `@BeforeEach`/test/`@AfterEach` cycles and one `@AfterAll`. Use them by cost and scope. `@BeforeAll`/`@AfterAll` are for expensive, shared, ideally immutable setup — starting a container or an embedded server, loading a large fixture — and releasing it. `@BeforeEach`/`@AfterEach` are for per-test state: constructing the object under test, seeding and truncating data, resetting mocks, closing resources the test opened. The important habit is that per-test setup belongs in `@BeforeEach`, not `@BeforeAll`, because anything shared and mutable makes tests order-dependent.

code

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

class OrderServiceTest {

    static Database db;
    OrderService service;

    @BeforeAll
    static void startDatabase() {      // once
        db = Database.startEmbedded();
    }

    @BeforeEach
    void freshService() {              // before every test
        db.truncateAll();
        service = new OrderService(db);
    }

    @AfterEach
    void closeService() {              // after every test
        service.close();
    }

    @AfterAll
    static void stopDatabase() {       // once
        db.stop();
    }

    @Test void placesOrder() { /* ... */ }
    @Test void rejectsEmptyOrder() { /* ... */ }
}

go deeper

for a junior

Recite the sequence accurately and give one concrete example of what goes in each hook.

for a middle

Add the cost-versus-mutability rule for choosing between the per-class and per-test hooks, and note teardown still runs after a failure.

for a senior

Talk about order independence as the property being protected, when sharing expensive fixtures is justified, and how hooks interact with parallel execution.

for a principal

Frame it as suite architecture: per-test isolation as the default contract, shared fixtures as an explicit, documented exception with immutability as the price of admission.

## The four annotations JUnit Jupiter (JUnit 5) defines four lifecycle annotations in `org.junit.jupiter.api`: - `@BeforeAll` — executed once for the test class, before any test method runs. - `@BeforeEach` — executed before **every** test method. - `@AfterEach` — executed after **every** test method. - `@AfterAll` — executed once for the test class, after all test methods have finished. They replace JUnit 4's `@BeforeClass`, `@Before`, `@After` and `@AfterClass` respectively. Methods must not be `private`, and must return `void`. ## The exact sequence For a class with tests `t1`, `t2`, `t3`: ``` @BeforeAll @BeforeEach → t1 → @AfterEach @BeforeEach → t2 → @AfterEach @BeforeEach → t3 → @AfterEach @AfterAll ``` Note what this implies about instance state. Under the default per-method test-instance behaviour a new test-class instance is created for each test, so fields assigned in `@BeforeEach` are re-initialised every time and nothing you store in an instance field can leak between tests. That is the property the whole design is protecting. ## What belongs where **`@BeforeEach`.** Anything the test needs freshly built: instantiating the class under test, wiring in test doubles, resetting a database to a known state, creating a temp fixture. The rule of thumb is that if a test could mutate it, it must be created per test. **`@AfterEach`.** Undo what a test could have changed outside its own object graph: close resources, truncate tables, stop a server the test started, restore a system property or a static registry, delete files. Also useful for on-failure diagnostics — dumping state before it disappears. **`@BeforeAll`.** Expensive setup shared by all tests: starting a Testcontainers instance, booting an embedded broker, loading a large read-only fixture, computing a costly derived value. It should ideally produce something immutable. **`@AfterAll`.** Releasing exactly what `@BeforeAll` acquired — stopping the container, closing the connection pool, shutting down the executor. ## Why the split matters Moving setup from `@BeforeEach` to `@BeforeAll` is the classic "optimisation" that quietly breaks a suite. Once two tests share mutable state, results depend on execution order: the suite passes in the IDE, fails in CI where order differs, or fails only when a single test is run in isolation because it depended on a predecessor. Debugging that is far more expensive than the milliseconds saved. Share only what is genuinely costly and genuinely read-only. The symmetric mistake is putting expensive setup in `@BeforeEach` — starting a Docker container per test turns a two-minute suite into twenty. The judgement is cost versus mutability, and it is exactly what an interviewer is probing. ## Related guarantees - Teardown hooks are executed even when the test or an earlier setup hook fails, so cleanup is reliable. - Multiple methods carrying the same annotation in the same class are all executed, but their relative order is deterministic-yet-unspecified; never write two `@BeforeEach` methods that depend on each other's ordering — merge them or call one from the other. - Hooks may accept resolved parameters (for example a `TestInfo`, or a `@TempDir Path`), which is how JUnit passes contextual information into setup. - `@BeforeAll`/`@AfterAll` must be `static` under the default per-method instance behaviour, because there is no single instance to invoke them on. ## How to answer Give the sequence crisply, then immediately say what belongs in each and why — the ordering is trivia, but "expensive-and-immutable goes in `@BeforeAll`, anything mutable goes in `@BeforeEach`" is the reasoning that shows you have maintained a real suite. Adding that teardown still runs after a failed setup, and that `@BeforeAll` must be static by default, rounds it out.

  • Two tests in a class both mutate an object created in @BeforeAll and one of them fails only when the full class runs. What is the fix?
    The shared mutable object makes the tests order-dependent: one test leaves state the other observes. Move its creation into @BeforeEach so each test gets a fresh instance, or, if construction is genuinely expensive, keep the costly part in @BeforeAll but derive a per-test copy in @BeforeEach. Never rely on execution order to make the pair pass.
  • What are the JUnit 4 equivalents of these four annotations?
    @BeforeAll corresponds to @BeforeClass, @BeforeEach to @Before, @AfterEach to @After and @AfterAll to @AfterClass. The semantics are the same; JUnit 5 renamed them for clarity and added parameter resolution to the hooks. The static requirement for the class-level hooks carried over from JUnit 4, except that Jupiter can lift it with a per-class test-instance setting.

saying these in an interview costs you the question

  • Saying @BeforeAll runs before each test rather than once per class
  • Putting per-test mutable setup in @BeforeAll to 'speed things up'
  • Believing @AfterEach is skipped when the test fails
  • Assuming multiple @BeforeEach methods in one class run in declaration order
  • Thinking @AfterAll runs after every test method

context

open as a page

In JUnit 5 (Jupiter), if a test class has three @Test methods, how many instances of that class does the framework create by default, and what is the reason for that design?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Three. Jupiter's default lifecycle is PER_METHOD: a brand-new instance of the test class is constructed before each @Test method. Instance fields therefore start fresh every time, so one test cannot leak state into another through fields.

open as a page

In JUnit 5 (Jupiter), what order are the @Test methods inside one test class executed in when you do not configure anything, and what does that imply for how those tests must be written?

level: juniorimportance: must knowfreq 58%

basics

~20 s

Jupiter applies no orderer by default, so methods run in a deterministic but intentionally non-obvious order — not source order, not alphabetical — and it may change between versions. Each test must therefore pass independently, in any order.

open as a page

Why does JUnit 5 normally require @BeforeAll and @AfterAll methods to be declared static, and what happens if you leave the keyword off?

level: middleimportance: must knowfreq 52%

basics

~20 s

By default JUnit creates a new test-class instance for every test method, so there is no single instance a class-level hook could belong to; it must be static to be callable once per class. Without static you get a runtime JUnitException telling you the method must be static, not a compile error.

open as a page

What changes in a JUnit 5 test class when you annotate it with @TestInstance(TestInstance.Lifecycle.PER_CLASS), and why would you want that?

level: middleimportance: must knowfreq 58%

basics

~20 s

Jupiter creates one instance of the class for all its test methods instead of one per method. Instance fields then survive between tests, and @BeforeAll/@AfterAll (and non-static @MethodSource factory methods) may be non-static because there is now an instance to own them.

open as a page

In JUnit 5, a test method lives in a class annotated @Nested inside an outer test class, and both classes declare @BeforeEach and @AfterEach methods. In what order do those four methods run around the test?

level: middleimportance: must knowfreq 55%

basics

~10 s

Outer @BeforeEach runs first, then the nested class's @BeforeEach, then the test, then the nested @AfterEach, then the outer @AfterEach. Setup runs outside-in, teardown inside-out — like nested try/finally blocks.

open as a page

How do you make JUnit 5 execute the test methods of a class in an order you control, and what exactly does the number you supply on each method mean?

level: middleimportance: must knowfreq 52%

basics

~20 s

Put @TestMethodOrder(MethodOrderer.OrderAnnotation.class) on the class and @Order(n) on the methods. Lower n runs first; methods without @Order get Integer.MAX_VALUE / 2, so they land between negative and large values. Ties keep their prior relative order.

open as a page

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?

level: middleimportance: should knowfreq 34%

basics

~20 s

Setup 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.

open as a page

Why must a class annotated @Nested in JUnit 5 be a non-static inner class, and what does that imply for fields that the enclosing class's setup methods initialize?

level: middleimportance: should knowfreq 44%

basics

~20 s

Because a non-static inner class instance holds a reference to an enclosing instance. JUnit builds the outer instance, runs its setup on it, then builds the inner instance bound to it — so nested tests can read and use the outer object's fields directly. A static nested class has no enclosing instance and is not treated as @Nested.

open as a page

Besides sorting by an explicit priority number, what other built-in strategies does JUnit 5 provide for sorting test methods, and how would you apply one across an entire codebase without editing every test class?

level: middleimportance: should knowfreq 34%

basics

~10 s

JUnit 5 ships MethodOrderer.OrderAnnotation, MethodName, DisplayName and Random. Apply one globally with the junit.jupiter.testmethod.order.default configuration parameter (for example in junit-platform.properties) instead of annotating each class. You can also implement MethodOrderer yourself.

open as a page

In JUnit 5, what happens to the rest of the run when an exception is thrown inside a @BeforeEach method, and how does that differ from an exception thrown in the test body or in an @AfterEach method?

level: seniorimportance: should knowfreq 28%

basics

~20 s

A failing @BeforeEach skips the remaining setup methods and the test body, and the test is reported as failed — but all @AfterEach methods still run. A failure in the test body still runs teardown. If several stages fail, JUnit reports the first as the primary failure with the others attached as suppressed exceptions.

open as a page

A JUnit 5 test class annotated @TestInstance(TestInstance.Lifecycle.PER_CLASS) passes when a single method is run in the IDE but fails when the whole class runs, and it fails intermittently once method-level parallel execution is switched on. What are the failure modes of holding mutable state in a shared test instance, and how do you contain them?

level: seniorimportance: should knowfreq 44%

basics

~20 s

With one instance for the class, mutations in one test are visible to the next, so tests become order-dependent (pass alone, fail together, or the reverse) and, under parallel methods, race on the same fields. Contain it: keep shared fields immutable after setup, reset mutable ones in @BeforeEach, and serialize the class with @Execution(SAME_THREAD) or @ResourceLock.

open as a page

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?

level: seniorimportance: should knowfreq 38%

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.

open as a page

How do you control the order in which JUnit 5 runs whole test classes, including inner classes marked @Nested, and what are the limits of that control?

level: seniorimportance: should knowfreq 26%

basics

~10 s

Use @TestClassOrder(ClassOrderer.OrderAnnotation.class) on an enclosing class to order its @Nested classes, with @Order on each nested class. Top-level classes are ordered only via the junit.jupiter.testclass.order.default configuration parameter. Built-in orderers: ClassName, DisplayName, OrderAnnotation, Random.

open as a page

Your JUnit 5 suite is slow because many test classes rebuild an expensive fixture — a started service, a large parsed document, a warmed client — before every test method. How would you decide between keeping the default one-instance-per-test-method lifecycle and moving classes to @TestInstance(PER_CLASS), and what guardrails would you require either way?

level: principalimportance: should knowfreq 30%

basics

~20 s

First check whether the cost is really per-instance; often it belongs in a fixture that can be built once and shared read-only. Move only classes whose expensive state is immutable after setup, keep everything mutable per test, and require guardrails: randomized method order, a run-each-test-alone job, and parallel execution enabled in CI.

open as a page

A JUnit 5 test class relies on a fixed execution order of its methods, and the team now wants to enable Jupiter's parallel execution for the whole suite. What actually breaks, and how do you decide between preserving the ordering and removing the dependency between those tests?

level: principalimportance: should knowfreq 22%

basics

~20 s

Under parallel execution an orderer fixes the order tests are started, not that one finishes before the next begins. Pin ordered classes to a single thread (@Execution(SAME_THREAD)) or guard shared state with @ResourceLock — then decide whether the sequence is genuinely worth losing parallelism and single-test reruns.

open as a page

How do you make PER_CLASS the default test instance lifecycle for an entire JUnit 5 suite without annotating every class, using the junit.jupiter.testinstance.lifecycle.default configuration parameter — and what should make you hesitate?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

Set the JUnit Platform configuration parameter junit.jupiter.testinstance.lifecycle.default to per_class — via a junit-platform.properties file on the test classpath, a JVM system property of the same name, or a launcher discovery request parameter. An explicit @TestInstance on a class still wins. Hesitate because it silently removes isolation from classes nobody reviewed.

open as a page

When would you model a test suite as a JUnit 5 @Nested class hierarchy that layers setup across levels, versus flat test classes with explicit fixture-builder methods? What degrades as the nesting gets deeper?

level: principalimportance: nice to knowfreq 26%

basics

~20 s

Nest when the scenarios form a real tree and each level adds one precondition — the layered @BeforeEach then reads as the story. Go flat with builder methods when preconditions are independent or the tree is deep: beyond two or three levels a reader must reconstruct state from several files' worth of hooks to understand one assertion.

open as a page