Close

JUnit 5 - Lifecycle Callbacks Extension

[Last Updated: Jan 1, 2026]

JUnit Jupiter provides several extension points that hook into the test execution lifecycle. These hooks allow you to execute logic before and after various phases, from the entire test class container to individual test methods.

Instead of simple print statements, extensions are typically used for cross-cutting concerns like measuring performance, managing external resources, or logging diagnostic data.

Lifecycle Extension points and Use Cases

  • BeforeAllCallback: Global setup like starting a Docker container or initializing a shared database connection.
  • BeforeEachCallback: Resetting the database state or clearing a cache before every test.
  • BeforeTestExecutionCallback: Capturing a high-precision timestamp immediately before the method runs (useful for profiling).
  • AfterTestExecutionCallback: Calculating elapsed time for the test logic only.
  • AfterEachCallback: General cleanup or logging test results.
  • AfterAllCallback: Releasing shared resources or closing the database connection.

Definition of BeforeAllCallback

Version: 6.0.1
 package org.junit.jupiter.api.extension;
 @FunctionalInterface
 @API(status = STABLE, since = "5.0")
 public interface BeforeAllCallback extends Extension {
     void beforeAll(ExtensionContext context) 1
                    throws Exception;
 }
1Callback that is invoked once before all tests in the current container.

Definition of BeforeEachCallback

Version: 6.0.1
 package org.junit.jupiter.api.extension;
 @FunctionalInterface
 @API(status = STABLE, since = "5.0")
 public interface BeforeEachCallback extends Extension {
     void beforeEach(ExtensionContext context) 1
                     throws Exception;
 }
1Callback that is invoked before an individual test and any user-defined setup methods for that test have been executed.

Definition of BeforeTestExecutionCallback

Version: 6.0.1
 package org.junit.jupiter.api.extension;
 @FunctionalInterface
 @API(status = STABLE, since = "5.0")
 public interface BeforeTestExecutionCallback extends Extension {
     void beforeTestExecution(ExtensionContext context) 1
                              throws Exception;
 }
1Callback that is invoked immediately before an individual test is executed but after any user-defined setup methods have been executed for that test.

Definition of AfterTestExecutionCallback

Version: 6.0.1
 package org.junit.jupiter.api.extension;
 @FunctionalInterface
 @API(status = STABLE, since = "5.0")
 public interface AfterTestExecutionCallback extends Extension {
     void afterTestExecution(ExtensionContext context) 1
                             throws Exception;
 }
1Callback that is invoked immediately after an individual test has been executed but before any user-defined teardown methods have been executed for that test.

Definition of AfterEachCallback

Version: 6.0.1
 package org.junit.jupiter.api.extension;
 @FunctionalInterface
 @API(status = STABLE, since = "5.0")
 public interface AfterEachCallback extends Extension {
     void afterEach(ExtensionContext context) 1
                    throws Exception;
 }
1Callback that is invoked after an individual test and any user-defined teardown methods for that test have been executed.

Definition of AfterAllCallback

Version: 6.0.1
 package org.junit.jupiter.api.extension;
 @FunctionalInterface
 @API(status = STABLE, since = "5.0")
 public interface AfterAllCallback extends Extension {
     void afterAll(ExtensionContext context) 1
                   throws Exception;
 }
1Callback that is invoked once after all tests in the current container.

Execution Order

It is important to understand where extension callbacks fits in the JUnit 5 lifecycle:

  1. BeforeAllCallback (Extension)
  2. @BeforeAll (Test class method)
  3. BeforeEachCallback (Extension)
  4. @BeforeEach (Test method)
  5. BeforeTestExecutionCallback (Extension)
  6. @Test / @ParameterizedTest / @RepeatedTest (Actual test execution)
  7. AfterTestExecutionCallback (Extension)
  8. @AfterEach (Test method)
  9. AfterEachCallback (Extension)
  10. @AfterAll (Test class method)
  11. AfterAllCallback (Extension)

Examples

A Simple Lifecycle Extension

Following example shows a simple extension (nested class) that just prints lifecycle events.

package com.logicbig.example;

import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.*;

@ExtendWith(SimpleLifecycleCallbacksTest.SimpleLifecycleExtension.class)
public class SimpleLifecycleCallbacksTest {

    @BeforeAll
    static void beforeAll(){
        System.out.println("Test Class: @BeforeAll method");
    }

    @BeforeEach
    void setup() {
        System.out.println("Test Class: @BeforeEach method");
    }

    @Test
    void myTest() {
        System.out.println("Test Class: @Test method");
    }

    @AfterEach
    void tearDown() {
        System.out.println("Test Class: @AfterEach method");
    }

    @AfterAll
    static void afterAll() {
        System.out.println("Test Class: @AfterAll method");
    }

    static class SimpleLifecycleExtension implements
            BeforeAllCallback, AfterAllCallback, 
            BeforeEachCallback, AfterEachCallback, 
            BeforeTestExecutionCallback, AfterTestExecutionCallback {

        @Override
        public void beforeAll(ExtensionContext c) {
            System.out.println("Extension Class: BeforeAllCallback"); }

        @Override
        public void afterAll(ExtensionContext c) {
            System.out.println("Extension Class: AfterAllCallback"); }

        @Override
        public void beforeEach(ExtensionContext c) {
            System.out.println("Extension Class: BeforeEachCallback"); }

        @Override
        public void afterEach(ExtensionContext c) {
            System.out.println("Extension Class: AfterEachCallback"); }

        @Override
        public void beforeTestExecution(ExtensionContext c) {
            System.out.println("Extension Class: BeforeTestExecutionCallback"); }

        @Override
        public void afterTestExecution(ExtensionContext c) {
            System.out.println("Extension Class: AfterTestExecutionCallback"); }
    }
}

Output

D:\example-projects\junit-5\junit-5-extensions\junit-5-lifecycle-callbacks>mvn test -Dtest=SimpleLifecycleCallbacksTest
[INFO] Scanning for projects...
[INFO]
[INFO] ----------< com.logicbig.example:junit-5-lifecycle-callbacks >----------
[INFO] Building junit-5-lifecycle-callbacks 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ junit-5-lifecycle-callbacks ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\example-projects\junit-5\junit-5-extensions\junit-5-lifecycle-callbacks\src\main\resources
[INFO]
[INFO] --- compiler:3.13.0:compile (default-compile) @ junit-5-lifecycle-callbacks ---
[INFO] No sources to compile
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ junit-5-lifecycle-callbacks ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\example-projects\junit-5\junit-5-extensions\junit-5-lifecycle-callbacks\src\test\resources
[INFO]
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ junit-5-lifecycle-callbacks ---
[INFO] Recompiling the module because of changed source code.
[WARNING] File encoding has not been set, using platform encoding windows-1252, i.e. build is platform dependent!
[INFO] Compiling 4 source files with javac [debug target 17] to target\test-classes
[INFO]
[INFO] --- surefire:3.5.0:test (default-test) @ junit-5-lifecycle-callbacks ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
Extension Class: BeforeAllCallback
Test Class: @BeforeAll method
Extension Class: BeforeEachCallback
Test Class: @BeforeEach method
Extension Class: BeforeTestExecutionCallback
Test Class: @Test method
Extension Class: AfterTestExecutionCallback
Test Class: @AfterEach method
Extension Class: AfterEachCallback
Test Class: @AfterAll method
Extension Class: AfterAllCallback
[INFO] +--com.logicbig.example.SimpleLifecycleCallbacksTest - 0.102 ss
[INFO] | '-- [OK] myTest - 0.031 ss
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.038 s
[INFO] Finished at: 2025-12-31T14:24:16+08:00
[INFO] ------------------------------------------------------------------------

Let's see a more practical example.

Performance Monitor Extension

In this example, we use the ExtensionContext.Store to pass state (start time) between callbacks without using class variables, ensuring thread safety.

package com.logicbig.example;

import org.junit.jupiter.api.extension.*;

public class PerformanceExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
    private static final String START_TIME = "START_TIME";

    @Override
    public void beforeTestExecution(ExtensionContext context) {
        context.getStore(ExtensionContext.Namespace.create(getClass(), context.getRequiredTestMethod()))
               .put(START_TIME, System.currentTimeMillis());
    }

    @Override
    public void afterTestExecution(ExtensionContext context) {
        long startTime = context.getStore(ExtensionContext.Namespace.create(getClass(), context.getRequiredTestMethod()))
                                .remove(START_TIME, long.class);
        long duration = System.currentTimeMillis() - startTime;
        System.out.println("Performance: " + context.getDisplayName() + " took " + duration + "ms");
    }
}

Extension simulating database access

package com.logicbig.example;

import org.junit.jupiter.api.extension.*;

public class DatabaseExtension implements BeforeAllCallback,
        AfterAllCallback, BeforeEachCallback {

    @Override
    public void beforeAll(ExtensionContext context) {
        System.out.println("DB: Starting connection pool");
    }

    @Override
    public void beforeEach(ExtensionContext context) {
        System.out.println("DB: Clearing tables for test: " + context.getDisplayName());
    }

    @Override
    public void afterAll(ExtensionContext context) {
        System.out.println("DB: Closing connection pool");
    }
}

Test Class

package com.logicbig.example;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith({DatabaseExtension.class, PerformanceExtension.class})
public class PracticalLifecycleTest {

    @Test
    void loadUserData() throws InterruptedException {
        Thread.sleep(100);
        System.out.println("  Test: User data loaded");
    }

    @Test
    void processOrder() throws InterruptedException {
        Thread.sleep(50);
        System.out.println("  Test: Order processed");
    }
}

Output

$ mvn test -Dtest=PracticalLifecycleTest
[INFO] Scanning for projects...
[INFO]
[INFO] ----------< com.logicbig.example:junit-5-lifecycle-callbacks >----------
[INFO] Building junit-5-lifecycle-callbacks 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ junit-5-lifecycle-callbacks ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\example-projects\junit-5\junit-5-extensions\junit-5-lifecycle-callbacks\src\main\resources
[INFO]
[INFO] --- compiler:3.13.0:compile (default-compile) @ junit-5-lifecycle-callbacks ---
[INFO] No sources to compile
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ junit-5-lifecycle-callbacks ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\example-projects\junit-5\junit-5-extensions\junit-5-lifecycle-callbacks\src\test\resources
[INFO]
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ junit-5-lifecycle-callbacks ---
[INFO] Nothing to compile - all classes are up to date.
[INFO]
[INFO] --- surefire:3.5.0:test (default-test) @ junit-5-lifecycle-callbacks ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
DB: Starting connection pool
DB: Clearing tables for test: loadUserData()
Test: User data loaded
Performance: loadUserData() took 124ms
DB: Clearing tables for test: processOrder()
Test: Order processed
Performance: processOrder() took 59ms
DB: Closing connection pool
[INFO] +--com.logicbig.example.PracticalLifecycleTest - 0.244 ss
[INFO] | +-- [OK] loadUserData - 0.173 ss
[INFO] | '-- [OK] processOrder - 0.060 ss
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.335 s
[INFO] Finished at: 2025-12-31T14:34:37+08:00
[INFO] ------------------------------------------------------------------------

The output confirms that the extensions successfully shared data using the JUnit Store API. The DatabaseExtension managed the high-level lifecycle, while the PerformanceExtension calculated the precise duration of the test method. This separation of concerns illustrates how extensions keep test classes clean by moving infrastructure logic into reusable components.

Example Project

Dependencies and Technologies Used:

  • junit-jupiter-engine 6.0.1 (Module "junit-jupiter-engine" of JUnit)
     Version Compatibility: 5.0.0 - 6.0.1Version List
    ×

    Version compatibilities of junit-jupiter-engine with this example:

    • 5.0.0
    • 5.0.1
    • 5.0.2
    • 5.0.3
    • 5.1.0
    • 5.1.1
    • 5.2.0
    • 5.3.0
    • 5.3.1
    • 5.3.2
    • 5.4.0
    • 5.4.1
    • 5.4.2
    • 5.5.0
    • 5.5.1
    • 5.5.2
    • 5.6.0
    • 5.6.1
    • 5.6.2
    • 5.6.3
    • 5.7.0
    • 5.7.1
    • 5.7.2
    • 5.8.0
    • 5.8.1
    • 5.8.2
    • 5.9.0
    • 5.9.1
    • 5.9.2
    • 5.9.3
    • 5.10.0
    • 5.10.1
    • 5.10.2
    • 5.10.3
    • 5.10.4
    • 5.10.5
    • 5.11.0
    • 5.11.1
    • 5.11.2
    • 5.11.3
    • 5.11.4
    • 5.12.0
    • 5.12.1
    • 5.12.2
    • 5.13.0
    • 5.13.1
    • 5.13.2
    • 5.13.3
    • 5.13.4
    • 5.14.0
    • 5.14.1
    • 6.0.0
    • 6.0.1

    Versions in green have been tested.

  • JDK 25
  • Maven 3.9.11

JUnit 5 - Lifecycle Callbacks Example Select All Download
  • junit-5-lifecycle-callbacks
    • src
      • test
        • java
          • com
            • logicbig
              • example
                • PracticalLifecycleTest.java

    See Also