Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions documentation/src/docs/asciidoc/link-attributes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ endif::[]
:ExtensionContext: {javadoc-root}/org/junit/jupiter/api/extension/ExtensionContext.html[ExtensionContext]
:ExtensionContext_Store: {javadoc-root}/org/junit/jupiter/api/extension/ExtensionContext.Store.html[Store]
:InvocationInterceptor: {javadoc-root}/org/junit/jupiter/api/extension/InvocationInterceptor.html[InvocationInterceptor]
:LifecycleMethodExecutionExceptionHandler: {javadoc-root}/org/junit/jupiter/api/extension/LifecycleMethodExecutionExceptionHandler.html[LifecycleMethodExecutionExceptionHandler]
:ParameterResolver: {javadoc-root}/org/junit/jupiter/api/extension/ParameterResolver.html[ParameterResolver]
:RegisterExtension: {javadoc-root}/org/junit/jupiter/api/extension/RegisterExtension.html[@RegisterExtension]
:TestExecutionExceptionHandler: {javadoc-root}/org/junit/jupiter/api/extension/TestExecutionExceptionHandler.html[TestExecutionExceptionHandler]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ on GitHub.
* New `InvocationInterceptor` extension API (see
<<../user-guide/index.adoc#extensions-intercepting-invocations, User Guide>> for
details).
* New extension APIs for handling exceptions during execution of lifecycle methods:
`@BeforeAll`, `@BeforeEach`, `@AfterEach` and `@AfterAll` (see
<<../user-guide/index.adoc#extensions-exception-handling, User Guide>> for
details)
* Added support for method URIs, e.g. `method:org.junit.Foo#bar()`, to `DynamicContainer`
and `DynamicTest` factory methods.

Expand Down
81 changes: 67 additions & 14 deletions documentation/src/docs/asciidoc/user-guide/extensions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -433,18 +433,51 @@ INFO: Method [sleep50ms] took 53 ms.
[[extensions-exception-handling]]
=== Exception Handling

`{TestExecutionExceptionHandler}` defines the API for `Extensions` that wish to handle
exceptions thrown during test execution.
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}`
and for those thrown during one of test lifecycle methods (`@BeforeAll`, `@BeforeEach`,
`@AfterEach` and `@AfterAll`) via `{LifecycleMethodExecutionExceptionHandler}`.

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

[source,java,indent=0]
.An exception handling extension
.An exception handling extension that filters IOExceptions in test execution
----
include::{testDir}/example/exception/IgnoreIOExceptionExtension.java[tags=user_guide]
----

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`.

[source,java,indent=0]
.An exception handling extension that records application state on error
----
include::{testDir}/example/exception/RecordStateOnErrorExtension.java[tags=user_guide]
----

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}` 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.

[source,java,indent=0]
.Registering multiple exception handling extensions
----
include::{testDir}/example/exception/MultipleHandlersTestCase.java[tags=user_guide]
----

[[extensions-intercepting-invocations]]
=== Intercepting Invocations

Expand Down Expand Up @@ -584,7 +617,7 @@ test method and will be repeated for every test method in the test class.
[#extensions-execution-order-diagram,reftext='{figure-caption}']
image::extensions_lifecycle.png[caption='',title='{figure-caption}']

The following table further explains the twelve steps in the
The following table further explains the sixteen steps in the
<<extensions-execution-order-diagram>> diagram.

[cols="5,15,80"]
Expand All @@ -600,48 +633,68 @@ The following table further explains the twelve steps in the
| user code executed before all tests of the container are executed

| 3
| interface `org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler
#handleBeforeAllMethodExecutionException`
| extension code for handling exceptions thrown during a method annotated with `@BeforeAll`

| 4
| interface `org.junit.jupiter.api.extension.BeforeEachCallback`
| extension code executed before each test is executed

| 4
| 5
| annotation `org.junit.jupiter.api.BeforeEach`
| user code executed before each test is executed

| 5
| 6
| interface `org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler
#handleBeforeEachMethodExecutionException`
| extension code for handling exceptions thrown during a method annotated with `@BeforeEach`

| 7
| interface `org.junit.jupiter.api.extension.BeforeTestExecutionCallback`
| extension code executed immediately before a test is executed

| 6
| 8
| annotation `org.junit.jupiter.api.Test`
| user code of the actual test method

| 7
| 9
| interface `org.junit.jupiter.api.extension.TestExecutionExceptionHandler`
| extension code for handling exceptions thrown during a test

| 8
| 10
| interface `org.junit.jupiter.api.extension.AfterTestExecutionCallback`
| extension code executed immediately after test execution and its corresponding exception handlers

| 9
| 11
| annotation `org.junit.jupiter.api.AfterEach`
| user code executed after each test is executed

| 10
| 12
| interface `org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler
#handleAfterEachMethodExecutionException`
| extension code for handling exceptions thrown during a method annotated with `@AfterEach`

| 13
| interface `org.junit.jupiter.api.extension.AfterEachCallback`
| extension code executed after each test is executed

| 11
| 14
| annotation `org.junit.jupiter.api.AfterAll`
| user code executed after all tests of the container are executed

| 12
| 15
| interface `org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler
#handleAfterAllMethodExecutionException`
| extension code for handling exceptions thrown during a method annotated with `@AfterAll`

| 16
| interface `org.junit.jupiter.api.extension.AfterAllCallback`
| extension code executed after all tests of the container are executed

|===

In the simplest case only the actual test method will be executed (step 6); all other
In the simplest case only the actual test method will be executed (step 8); all other
steps are optional depending on the presence of user code or extension support for the
corresponding lifecycle callback. For further details on the various lifecycle callbacks
please consult the respective Javadoc for each annotation and extension.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2015-2019 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
* accompanies this distribution and is available at
*
* https://www.eclipse.org/legal/epl-v20.html
*/

package example.exception;

import static example.exception.MultipleHandlersTestCase.ThirdExecutedHandler;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler;
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;

// @formatter:off
// tag::user_guide[]
// Register handlers for @Test, @BeforeEach, @AfterEach as well as @BeforeAll and @AfterAll
@ExtendWith(ThirdExecutedHandler.class)
class MultipleHandlersTestCase {

// Register handlers for @Test, @BeforeEach, @AfterEach only
@ExtendWith(SecondExecutedHandler.class)
@ExtendWith(FirstExecutedHandler.class)
@Test
void testMethod() {
}

// end::user_guide[]

static class FirstExecutedHandler implements TestExecutionExceptionHandler {
@Override
public void handleTestExecutionException(ExtensionContext context, Throwable ex)
throws Throwable {
throw ex;
}
}

static class SecondExecutedHandler implements LifecycleMethodExecutionExceptionHandler {
@Override
public void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable ex)
throws Throwable {
throw ex;
}
}

static class ThirdExecutedHandler implements LifecycleMethodExecutionExceptionHandler {
@Override
public void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable ex)
throws Throwable {
throw ex;
}
}
// tag::user_guide[]
}
// end::user_guide[]
// @formatter:on
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2015-2019 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
* accompanies this distribution and is available at
*
* https://www.eclipse.org/legal/epl-v20.html
*/

package example.exception;

import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler;

// @formatter:off
// tag::user_guide[]
class RecordStateOnErrorExtension implements LifecycleMethodExecutionExceptionHandler {

@Override
public void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable ex)
throws Throwable {
memoryDumpForFurtherInvestigation("Failure recorded during class setup");
throw ex;
}

@Override
public void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable ex)
throws Throwable {
memoryDumpForFurtherInvestigation("Failure recorded during test setup");
throw ex;
}

@Override
public void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable ex)
throws Throwable {
memoryDumpForFurtherInvestigation("Failure recorded during test cleanup");
throw ex;
}

@Override
public void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable ex)
throws Throwable {
memoryDumpForFurtherInvestigation("Failure recorded during class cleanup");
throw ex;
}
// end::user_guide[]

private void memoryDumpForFurtherInvestigation(String error) {

}
// tag::user_guide[]
}
// end::user_guide[]
// @formatter:on
Loading