Close

JUnit 5 - InvocationInterceptor Extension

[Last Updated: Jan 1, 2026]

The InvocationInterceptor is a powerful extension point used to intercept calls to test methods, constructors, and lifecycle methods. Unlike callbacks that run before or after an event, this one completely wraps the actual invocation. Think of it like an 'Around Advice' in Aspect-Oriented Programming (AOP); you are handed an Invocation object, and the execution only proceeds if you explicitly call invocation.proceed().

Java source and doc

Definition of InvocationInterceptor

Version: 6.0.1
 package org.junit.jupiter.api.extension;
 @API(status = STABLE, since = "5.10")
 public interface InvocationInterceptor extends TestInstantiationAwareExtension {
     default <T> T interceptTestClassConstructor(Invocation<T> invocation, 1
                                                 ReflectiveInvocationContext<Constructor<T>> invocationContext,
                                                 ExtensionContext extensionContext)
                                                 throws Throwable;
     default void interceptBeforeAllMethod(Invocation<@Nullable Void> invocation, 2
                                           ReflectiveInvocationContext<Method> invocationContext,
                                           ExtensionContext extensionContext)
                                           throws Throwable;
     default void interceptBeforeEachMethod(Invocation<@Nullable Void> invocation, 3
                                            ReflectiveInvocationContext<Method> invocationContext,
                                            ExtensionContext extensionContext)
                                            throws Throwable;
     default void interceptTestMethod(Invocation<@Nullable Void> invocation, 4
                                      ReflectiveInvocationContext<Method> invocationContext,
                                      ExtensionContext extensionContext)
                                      throws Throwable;
     default <T extends @Nullable Object> T interceptTestFactoryMethod(Invocation<T> invocation, 5
                                                                       ReflectiveInvocationContext<Method> invocationContext,
                                                                       ExtensionContext extensionContext)
                                                                       throws Throwable;
     default void interceptTestTemplateMethod(Invocation<@Nullable Void> invocation, 6
                                              ReflectiveInvocationContext<Method> invocationContext,
                                              ExtensionContext extensionContext)
                                              throws Throwable;
     default void interceptDynamicTest(Invocation<@Nullable Void> invocation, 7
                                       DynamicTestInvocationContext invocationContext,
                                       ExtensionContext extensionContext)
                                       throws Throwable;
     default void interceptAfterEachMethod(Invocation<@Nullable Void> invocation, 8
                                           ReflectiveInvocationContext<Method> invocationContext,
                                           ExtensionContext extensionContext)
                                           throws Throwable;
     default void interceptAfterAllMethod(Invocation<@Nullable Void> invocation, 9
                                          ReflectiveInvocationContext<Method> invocationContext,
                                          ExtensionContext extensionContext)
                                          throws Throwable;
     @API(status = STABLE, since = "5.10")
     interface Invocation<T extends @Nullable Object> {
         /**
          * Proceed with this invocation.
          *
          * @return the result of this invocation; potentially {@code null}.
          * @throws Throwable in case the invocation failed
          */
         T proceed()
                   throws Throwable;
         /**
          * Explicitly skip this invocation.
          *
          * <p>This allows to bypass the check that {@link #proceed()} must be
          * called at least once. The default implementation does nothing.
          */
         default void skip();
     }
 }
1Intercept the invocation of a test class constructor.
2Intercept the invocation of a @BeforeAll method.
3Intercept the invocation of a @BeforeEach method.
4Intercept the invocation of a @Test method.
5Intercept the invocation of a @TestFactory method, such as a {@link org.
6Intercept the invocation of a @TestTemplate method.
7Intercept the invocation of a DynamicTest.
8Intercept the invocation of an @AfterEach method.
9Intercept the invocation of an @AfterAll method.

Example

package com.logicbig.example;

import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.InvocationInterceptor;
import org.junit.jupiter.api.extension.ReflectiveInvocationContext;
import java.lang.reflect.Method;

public class PerformanceInterceptor implements InvocationInterceptor {

    @Override
    public void interceptTestMethod(Invocation<Void> invocation,
                                    ReflectiveInvocationContext<Method> invocationContext,
                                    ExtensionContext extensionContext) throws Throwable {

        long startTime = System.currentTimeMillis();

        try {
            invocation.proceed();
        } finally {
            long duration = System.currentTimeMillis() - startTime;
            String testName = extensionContext.getDisplayName();

            System.out.println(String.format("Test [%s] took %d ms", testName, duration));
        }
    }
}

Test Class

package com.logicbig.example;

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

@ExtendWith(PerformanceInterceptor.class)
public class MyTest {

    @Test
    void fastTest() {
        System.out.println("Running a quick operation...");
        // Finishes almost instantly
    }

    @Test
    void slowTest() throws InterruptedException {
        System.out.println("Running a heavy operation...");
        // Artificially slowing down the test
        Thread.sleep(250); 
    }
}

Output

$ mvn test -Dtest=MyTest
[INFO] Scanning for projects...
[INFO]
[INFO] --------< com.logicbig.example:junit-5-invocation-interceptor >---------
[INFO] Building junit-5-invocation-interceptor 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ junit-5-invocation-interceptor ---
[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-invocation-interceptor\src\main\resources
[INFO]
[INFO] --- compiler:3.13.0:compile (default-compile) @ junit-5-invocation-interceptor ---
[INFO] No sources to compile
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ junit-5-invocation-interceptor ---
[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-invocation-interceptor\src\test\resources
[INFO]
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ junit-5-invocation-interceptor ---
[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 2 source files with javac [debug target 17] to target\test-classes
[INFO]
[INFO] --- surefire:3.5.0:test (default-test) @ junit-5-invocation-interceptor ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
Running a heavy operation...
Test [slowTest()] took 265 ms
Running a quick operation...
Test [fastTest()] took 1 ms
[INFO] +--com.logicbig.example.MyTest - 0.355 ss
[INFO] | +-- [OK] slowTest - 0.314 ss
[INFO] | '-- [OK] fastTest - 0.011 ss
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.177 s
[INFO] Finished at: 2026-01-01T02:21:42+08:00
[INFO] ------------------------------------------------------------------------

The output confirms that our InvocationInterceptor is successfully wrapping the test execution and capturing fine-grained timing data.

Example Project

Dependencies and Technologies Used:

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

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

    • 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 - InvocationInterceptor Select All Download
  • junit-5-invocation-interceptor
    • src
      • test
        • java
          • com
            • logicbig
              • example
                • PerformanceInterceptor.java

    See Also