5.10. Exception Handling

Exceptions thrown during the test execution may be intercepted and handled accordingly before propagating further, so that certain actions like error logging or resource releasing may be defined in specialized Extensions. JUnit Jupiter offers API for Extensions that wish to handle exceptions thrown during @Test methods via [TestExecutionExceptionHandler](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestExecutionExceptionHandler.html) and for those thrown during one of test lifecycle methods (@BeforeAll, @BeforeEach, @AfterEach and @AfterAll) via [LifecycleMethodExecutionExceptionHandler](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/extension/LifecycleMethodExecutionExceptionHandler.html).

The following example shows an extension which will swallow all instances of IOException but rethrow any other type of exception.

An exception handling extension that filters IOExceptions in test execution

  1. public class IgnoreIOExceptionExtension implements TestExecutionExceptionHandler {
  2. @Override
  3. public void handleTestExecutionException(ExtensionContext context, Throwable throwable)
  4. throws Throwable {
  5. if (throwable instanceof IOException) {
  6. return;
  7. }
  8. throw throwable;
  9. }
  10. }

Another example shows how to record the state of an application under test exactly at the point of unexpected exception being thrown during setup and cleanup. Note that unlike relying on lifecycle callbacks, which may or may not be executed depending on the test status, this solution guarantees execution immediately after failing @BeforeAll, @BeforeEach, @AfterEach or @AfterAll.

An exception handling extension that records application state on error

  1. class RecordStateOnErrorExtension implements LifecycleMethodExecutionExceptionHandler {
  2. @Override
  3. public void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable ex)
  4. throws Throwable {
  5. memoryDumpForFurtherInvestigation("Failure recorded during class setup");
  6. throw ex;
  7. }
  8. @Override
  9. public void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable ex)
  10. throws Throwable {
  11. memoryDumpForFurtherInvestigation("Failure recorded during test setup");
  12. throw ex;
  13. }
  14. @Override
  15. public void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable ex)
  16. throws Throwable {
  17. memoryDumpForFurtherInvestigation("Failure recorded during test cleanup");
  18. throw ex;
  19. }
  20. @Override
  21. public void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable ex)
  22. throws Throwable {
  23. memoryDumpForFurtherInvestigation("Failure recorded during class cleanup");
  24. throw ex;
  25. }
  26. }

Multiple execution exception handlers may be invoked for the same lifecycle method in order of declaration. If one of the handlers swallows the handled exception, subsequent ones will not be executed, and no failure will be propagated to JUnit engine, as if the exception was never thrown. Handlers may also choose to rethrow the exception or throw a different one, potentially wrapping the original.

Extensions implementing [LifecycleMethodExecutionExceptionHandler](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/extension/LifecycleMethodExecutionExceptionHandler.html) that wish to handle exceptions thrown during @BeforeAll or @AfterAll need to be registered on a class level, while handlers for BeforeEach and AfterEach may be also registered for individual test methods.

Registering multiple exception handling extensions

  1. // Register handlers for @Test, @BeforeEach, @AfterEach as well as @BeforeAll and @AfterAll
  2. @ExtendWith(ThirdExecutedHandler.class)
  3. class MultipleHandlersTestCase {
  4. // Register handlers for @Test, @BeforeEach, @AfterEach only
  5. @ExtendWith(SecondExecutedHandler.class)
  6. @ExtendWith(FirstExecutedHandler.class)
  7. @Test
  8. void testMethod() {
  9. }
  10. }