The LifecycleMethodExecutionExceptionHandler is a specialized extension point used to handle exceptions that occur within JUnit 5 lifecycle methods, such as @BeforeEach, @AfterEach, @BeforeAll, or @AfterAll. By default, an exception in a setup method prevents the test from running; this handler allows you to intercept that behavior.
Java source and doc
Definition of LifecycleMethodExecutionExceptionHandlerVersion: 6.0.1 package org.junit.jupiter.api.extension;
@API(status = STABLE, since = "5.10")
public interface LifecycleMethodExecutionExceptionHandler extends Extension {
default void handleBeforeAllMethodExecutionException(ExtensionContext context, 1
Throwable throwable)
throws Throwable;
default void handleBeforeEachMethodExecutionException(ExtensionContext context, 2
Throwable throwable)
throws Throwable;
default void handleAfterEachMethodExecutionException(ExtensionContext context, 3
Throwable throwable)
throws Throwable;
default void handleAfterAllMethodExecutionException(ExtensionContext context, 4
Throwable throwable)
throws Throwable;
}
Example
In this example, we implement LifecycleMethodExecutionExceptionHandler . If an IOException occurs during the @BeforeEach phase (simulating a failure to write a temporary log file), the extension swallows the exception. This ensures the test method still executes despite the non-critical setup failure.
package com.logicbig.example;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler;
import java.io.IOException;
public class MyLifecycleExceptionHandler implements LifecycleMethodExecutionExceptionHandler {
@Override
public void handleBeforeEachMethodExecutionException(ExtensionContext context,
Throwable throwable) throws Throwable {
if (throwable instanceof IOException) {
System.err.println("⚠� Setup Warning: IO issue encountered, but proceeding with test: "
+ throwable.getMessage());
return; // Swallow exception
}
throw throwable;
}
@Override
public void handleAfterEachMethodExecutionException(ExtensionContext context,
Throwable throwable) throws Throwable {
if (throwable instanceof UnsupportedOperationException) {
System.err.println("⚠� Cleanup Note: Operation not supported on this platform.");
return;
}
throw throwable;
}
}
Test Class
package com.logicbig.example;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import java.io.IOException;
@ExtendWith(MyLifecycleExceptionHandler.class)
public class LifecycleHandlerTest {
@BeforeEach
void setup() throws IOException {
System.out.println("Running @BeforeEach setup...");
// This would normally crash the whole test
throw new IOException("Failed to write transient test log");
}
@Test
void importantTest() {
System.out.println(" Executing core test logic...");
Assertions.assertTrue(true);
}
@AfterEach
void cleanup() {
System.out.println("Running @AfterEach cleanup...");
throw new UnsupportedOperationException("Cleanup not implemented yet");
}
}
Output$ mvn test -Dtest=LifecycleHandlerTest [INFO] Scanning for projects... [INFO] [INFO] ------< com.logicbig.example:junit-5-lifecycle-exception-handler >------ [INFO] Building junit-5-lifecycle-exception-handler 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ junit-5-lifecycle-exception-handler --- [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-exception-handler\src\main\resources [INFO] [INFO] --- compiler:3.13.0:compile (default-compile) @ junit-5-lifecycle-exception-handler --- [INFO] No sources to compile [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ junit-5-lifecycle-exception-handler --- [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-exception-handler\src\test\resources [INFO] [INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ junit-5-lifecycle-exception-handler --- [INFO] Nothing to compile - all classes are up to date. [INFO] [INFO] --- surefire:3.2.5:test (default-test) @ junit-5-lifecycle-exception-handler --- [INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.logicbig.example.LifecycleHandlerTest Running @BeforeEach setup... Executing core test logic... Running @AfterEach cleanup... [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.122 s -- in com.logicbig.example.LifecycleHandlerTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.514 s [INFO] Finished at: 2025-12-31T20:05:50+08:00 [INFO] ------------------------------------------------------------------------ ⚠�? Setup Warning: IO issue encountered, but proceeding with test: Failed to write transient test log ⚠�? Cleanup Note: Operation not supported on this platform.
Output Analysis
As observed in the execution logs, even though the setup() method threw an IOException, the test method importantTest() still ran and passed. This confirms that the extension successfully intercepted the lifecycle failure and allowed the test engine to proceed. This approach is ideal for managing flaky auxiliary resources that are not core to the test's validity.
Example ProjectDependencies and Technologies Used: - junit-jupiter-engine 6.0.1 (Module "junit-jupiter-engine" of JUnit)
Version Compatibility: 5.0.0 - 6.0.1 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
|