skip to content

Extension Callbacks

The lifecycle callback interfaces an extension can implement and the exact order they fire around a test. The core 'how does the Extension API work' interview question.

on this pageshow

questions

6

JUnit 5's extension model defines BeforeAllCallback, BeforeEachCallback, BeforeTestExecutionCallback and their After* counterparts. In what order do these run around a single test method, and how does that order change when several extensions are registered?

level: middleimportance: must knowfreq 45%

answer

  1. BeforeAll→@BeforeAll→BeforeEach→@BeforeEach→BeforeTestExecution→test
  2. After* mirrors, TestExecution pair innermost
  3. extension callbacks wrap the user's annotated methods
  4. multiple extensions: before in order, after in reverse (onion)
  5. After* still runs after a failure; exceptions aggregated

basics

~10 s

Order is BeforeAllCallback, @BeforeAll, BeforeEachCallback, @BeforeEach, BeforeTestExecutionCallback, the test, AfterTestExecutionCallback, @AfterEach, AfterEachCallback, @AfterAll, AfterAllCallback. Multiple extensions nest like an onion: Before* in registration order, After* in reverse.

solid answer

~50 s

For one test method the documented sequence is: 1. `BeforeAllCallback` 2. `@BeforeAll` methods 3. `BeforeEachCallback` 4. `@BeforeEach` methods 5. `BeforeTestExecutionCallback` 6. the `@Test` method 7. `TestExecutionExceptionHandler` (only if it threw) 8. `AfterTestExecutionCallback` 9. `@AfterEach` methods 10. `AfterEachCallback` 11. `@AfterAll` methods 12. `AfterAllCallback` So extension callbacks **wrap** the user's annotated lifecycle methods on the outside, except the `*TestExecution*` pair, which sits **inside** `@BeforeEach`/`@AfterEach` and hugs the test invocation itself — that is why timing and per-invocation instrumentation belong there. With several extensions, JUnit applies onion semantics: `Before*` callbacks run in registration order, `After*` callbacks in reverse, so each extension tears down inside the one registered before it. Registration order for declarative extensions follows the `@ExtendWith` declarations; `@Order` on `@RegisterExtension` fields controls it explicitly. `After*` callbacks still run when earlier steps fail, and failures are aggregated rather than short-circuiting the rest of the teardown.

code

java · 18 lines
java
class TimingExtension implements BeforeEachCallback, BeforeTestExecutionCallback,
                                 AfterTestExecutionCallback, AfterEachCallback {
    private long start;

    @Override public void beforeEach(ExtensionContext ctx) {
        // runs BEFORE the user's @BeforeEach — prepare what their setup needs
    }
    @Override public void beforeTestExecution(ExtensionContext ctx) {
        start = System.nanoTime();               // brackets the test body only
    }
    @Override public void afterTestExecution(ExtensionContext ctx) {
        long ms = (System.nanoTime() - start) / 1_000_000;
        ctx.publishReportEntry("durationMs", String.valueOf(ms));
    }
    @Override public void afterEach(ExtensionContext ctx) {
        // runs AFTER the user's @AfterEach — safe place to release resources
    }
}

go deeper

for a junior

Recite the sequence and the key fact that extension callbacks sit outside the user's @BeforeEach/@AfterEach, with the TestExecution pair innermost.

for a middle

Explain why you would pick each ring — user setup dependencies, timing the body only, cleanup that must outlive teardown — and the reverse ordering of After* callbacks.

for a senior

Add composition: registration order sources, @Order on @RegisterExtension, nesting and inheritance, and the guarantee that teardown still runs and exceptions aggregate.

for a principal

Discuss it as a composition contract: designing extensions that nest safely with third-party ones, avoiding hidden ordering dependencies, and choosing container-scoped versus test-scoped rings for expensive resources.

## Why the order matters An extension is a class implementing one or more callback interfaces from `org.junit.jupiter.api.extension`. Once registered, JUnit invokes it at fixed points around test execution. Getting the order right is the difference between an extension that reliably sets up a database transaction, a mock server or a clock, and one that races the user's own `@BeforeEach`. ## The canonical sequence For a single test method in a class, JUnit Jupiter documents this relative order: 1. **`BeforeAllCallback`** — once per container, before any user class-level setup. 2. **`@BeforeAll`** — the user's static setup methods. 3. *(test instance creation and `TestInstancePostProcessor` for the default per-method lifecycle)* 4. **`BeforeEachCallback`** — extension setup per test. 5. **`@BeforeEach`** — the user's per-test setup. 6. **`BeforeTestExecutionCallback`** — immediately before the test method runs. 7. **the `@Test` method body** 8. **`TestExecutionExceptionHandler`** — only if the body threw. 9. **`AfterTestExecutionCallback`** — immediately after the body. 10. **`@AfterEach`** 11. **`AfterEachCallback`** 12. **`@AfterAll`** 13. **`AfterAllCallback`** Read it as concentric rings. The outermost ring is `BeforeAllCallback`/`AfterAllCallback`; inside it the user's `@BeforeAll`/`@AfterAll`; inside that `BeforeEachCallback`/`AfterEachCallback`; then `@BeforeEach`/`@AfterEach`; and innermost, hugging the invocation, `BeforeTestExecutionCallback`/`AfterTestExecutionCallback`. ## Which ring to pick The practical consequences: - **`BeforeEachCallback` runs before the user's `@BeforeEach`.** So if a test's own setup needs something your extension provides — an open transaction, a started container, a fixed clock — that must be done in `BeforeEachCallback`, not in `BeforeTestExecutionCallback`, which is too late. - **`AfterEachCallback` runs after the user's `@AfterEach`.** Cleanup that must outlive the user's teardown (closing a transaction the user's code still needed, stopping a server) belongs there. - **The `*TestExecution*` pair brackets only the test body.** It excludes user setup and teardown, which makes it the correct home for measuring the duration of the test itself, capturing a screenshot at the exact moment of failure, or asserting on state produced strictly by the body. `AfterTestExecutionCallback` can also inspect the outcome via the extension context's execution exception. ## Multiple extensions: onion, not queue When several extensions implement the same callback, JUnit wraps rather than chains: **`Before*` callbacks are invoked in registration order and `After*` callbacks in reverse registration order.** Extension A registered first therefore sets up first and tears down last, guaranteeing that B's teardown still sees A's setup — the same discipline as nested try/finally blocks or constructor/destructor ordering. Registration order comes from where the extension was declared: declarative `@ExtendWith` declarations are applied in the order they appear (class-level before method-level; superclass and enclosing-class registrations before the current class's). Programmatic extensions registered on `@RegisterExtension` fields are ordered deterministically and can be sequenced explicitly with `@Order`. Extensions registered automatically through the `ServiceLoader` mechanism come first of all when that feature is enabled. Any extension type is registered at most once per context, so declaring the same one twice does not double the callbacks. ## Inheritance and nesting User lifecycle methods follow their own inherited order: superclass `@BeforeEach` before subclass `@BeforeEach`, subclass `@AfterEach` before superclass `@AfterEach`. For `@Nested` classes, the outer class's `@BeforeEach` and the outer registrations run before the inner ones, again unwinding in reverse — extensions registered on an outer class apply to nested classes too. ## Failure behaviour This is what makes the model usable for resource management: **`After*` callbacks still run when something earlier failed.** If `@BeforeEach` throws, the test is not executed, but the `After*` chain unwinds so resources are released. Exceptions thrown by multiple callbacks are aggregated and reported together (as suppressed exceptions on the primary failure) rather than one silently swallowing another. That means your teardown must be defensive — it can be invoked in a state where its corresponding setup partly failed, so null-check and guard accordingly. ## A short mental checklist when writing an extension - Does the user's `@BeforeEach` need my state? → `BeforeEachCallback`. - Do I need to bracket only the test body (timing, screenshots, state diff)? → `BeforeTestExecutionCallback`/`AfterTestExecutionCallback`. - Must my cleanup outlive the user's teardown? → `AfterEachCallback`. - Is the resource expensive and shareable across the whole class? → `BeforeAllCallback`/`AfterAllCallback`. - Am I composing with another extension that must wrap mine? → control registration order (`@Order` on `@RegisterExtension`, or declaration sequence).

  • You need an extension to open a database transaction that the test's own @BeforeEach fixture inserts into. Which callback do you implement, and why not BeforeTestExecutionCallback?
    Implement BeforeEachCallback, because it runs before the user's @BeforeEach, so their fixture inserts inside your transaction. BeforeTestExecutionCallback fires after all user setup and immediately before the test body, by which point the fixture would already have been written outside the transaction. Roll back in AfterEachCallback, which runs after the user's @AfterEach.
  • Two extensions are registered on the same class, A then B. If B's afterEach throws, does A's afterEach still run?
    Yes. After* callbacks unwind in reverse registration order and JUnit continues the chain even when one throws, aggregating the failures so none is silently lost. That is what makes the model safe for resource cleanup — you should still write defensive teardown, since your setup may have failed partway.

Concentric rings, or nested try/finally: the first extension in is the last one out, and the TestExecution pair is the innermost ring, touching the test body itself.

saying these in an interview costs you the question

  • Believing extension BeforeEachCallback runs after the user's @BeforeEach
  • Thinking BeforeTestExecutionCallback and BeforeEachCallback are interchangeable
  • Assuming After* callbacks are skipped when setup or the test failed
  • Expecting multiple extensions to tear down in registration order rather than reverse

context

open as a page

When would you implement JUnit 5's BeforeEachCallback/AfterEachCallback extension interfaces instead of just writing @BeforeEach and @AfterEach methods in the test class, and what do you gain?

level: middleimportance: should knowfreq 35%

basics

~20 s

Use callbacks when the setup is cross-cutting and reusable across many test classes, needs to wrap the tests' own setup, or must be composed with other extensions. Annotated methods are fine for fixture code specific to one class.

open as a page

JUnit 5 provides TestExecutionExceptionHandler as an extension callback. What can it do with a failing test, what are its limits, and where would you use it responsibly?

level: seniorimportance: should knowfreq 30%

basics

~20 s

It intercepts a throwable from a @Test method body and may swallow it (return normally, test passes), rethrow it, or rethrow a different one. It does not see exceptions from @BeforeEach/@AfterAll — those need LifecycleMethodExecutionExceptionHandler.

open as a page

JUnit 5's extension model includes InvocationInterceptor. How does it differ from the simple before/after callbacks, what must an implementation always do, and what problems does it solve that callbacks cannot?

level: seniorimportance: nice to knowfreq 25%

basics

~20 s

InvocationInterceptor wraps the invocation itself rather than bracketing it, receiving an Invocation object it must either proceed() or skip(). That lets it surround the call with try/catch/finally, retry it, or run it on another thread — things separate before/after callbacks cannot express.

open as a page

JUnit 5's extension model includes TestInstancePostProcessor. What is it for, when in the lifecycle does it run, and how does the per-class test instance lifecycle change its behaviour?

level: seniorimportance: nice to knowfreq 25%

basics

~20 s

TestInstancePostProcessor lets an extension act on a freshly created test instance — typically injecting into annotated fields. It runs right after instantiation, before BeforeEachCallback. With the default per-method lifecycle it runs for every test; with per-class lifecycle it runs once.

open as a page

You are designing a reusable JUnit 5 extension that gives every integration test a clean database, a stubbed HTTP dependency and diagnostics on failure. How do you decide which callback interfaces to implement, and what failure modes do you design against?

level: principalimportance: nice to knowfreq 20%

basics

~20 s

Map each concern to lifecycle scope: expensive shared resources in BeforeAll/AfterAll callbacks, per-test isolation in BeforeEach/AfterEach callbacks, diagnostics via the exception handler plus AfterTestExecution. Design for teardown that always runs, deterministic ordering, and honest reporting.

open as a page