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 InvocationInterceptorVersion: 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();
}
}
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 ProjectDependencies and Technologies Used: - junit-jupiter-engine 6.0.1 (Module "junit-jupiter-engine" of JUnit)
Version Compatibility: 5.5.0 - 6.0.1 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
|
|