Skip to content

Commit d1cf4cc

Browse files
committed
Add user guide and release notes entries on LifecycleMethodExecutionExceptionHandler
Issue #1454
1 parent 2458189 commit d1cf4cc

File tree

6 files changed

+185
-14
lines changed

6 files changed

+185
-14
lines changed

documentation/src/docs/asciidoc/link-attributes.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ endif::[]
8181
:ExtensionContext: {javadoc-root}/org/junit/jupiter/api/extension/ExtensionContext.html[ExtensionContext]
8282
:ExtensionContext_Store: {javadoc-root}/org/junit/jupiter/api/extension/ExtensionContext.Store.html[Store]
8383
:InvocationInterceptor: {javadoc-root}/org/junit/jupiter/api/extension/InvocationInterceptor.html[InvocationInterceptor]
84+
:LifecycleMethodExecutionExceptionHandler: {javadoc-root}/org/junit/jupiter/api/extension/LifecycleMethodExecutionExceptionHandler.html[LifecycleMethodExecutionExceptionHandler]
8485
:ParameterResolver: {javadoc-root}/org/junit/jupiter/api/extension/ParameterResolver.html[ParameterResolver]
8586
:RegisterExtension: {javadoc-root}/org/junit/jupiter/api/extension/RegisterExtension.html[@RegisterExtension]
8687
:TestExecutionExceptionHandler: {javadoc-root}/org/junit/jupiter/api/extension/TestExecutionExceptionHandler.html[TestExecutionExceptionHandler]

documentation/src/docs/asciidoc/release-notes/release-notes-5.5.0-RC1.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ on GitHub.
7979
* New `InvocationInterceptor` extension API (see
8080
<<../user-guide/index.adoc#extensions-intercepting-invocations, User Guide>> for
8181
details).
82+
* New extension APIs for handling exceptions during execution of lifecycle methods:
83+
`@BeforeAll`, `@BeforeEach`, `@AfterEach` and `@AfterAll` (see
84+
<<../user-guide/index.adoc#extensions-exception-handling, User Guide>> for
85+
details)
8286
* Added support for method URIs, e.g. `method:org.junit.Foo#bar()`, to `DynamicContainer`
8387
and `DynamicTest` factory methods.
8488

documentation/src/docs/asciidoc/user-guide/extensions.adoc

Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -433,18 +433,51 @@ INFO: Method [sleep50ms] took 53 ms.
433433
[[extensions-exception-handling]]
434434
=== Exception Handling
435435

436-
`{TestExecutionExceptionHandler}` defines the API for `Extensions` that wish to handle
437-
exceptions thrown during test execution.
436+
Exceptions thrown during the test execution may be intercepted and handled accordingly
437+
before propagating further, so that certain actions like error logging or resource releasing
438+
may be defined in specialized `Extensions`. JUnit Jupiter offers API for `Extensions` that
439+
wish to handle exceptions thrown during `@Test` methods via `{TestExecutionExceptionHandler}`
440+
and for those thrown during one of test lifecycle methods (`@BeforeAll`, `@BeforeEach`,
441+
`@AfterEach` and `@AfterAll`) via `{LifecycleMethodExecutionExceptionHandler}`.
438442

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

442446
[source,java,indent=0]
443-
.An exception handling extension
447+
.An exception handling extension that filters IOExceptions in test execution
444448
----
445449
include::{testDir}/example/exception/IgnoreIOExceptionExtension.java[tags=user_guide]
446450
----
447451

452+
Another example shows how to record the state of an application under test exactly at
453+
the point of unexpected exception being thrown during setup and cleanup. Note that unlike
454+
relying on lifecycle callbacks, which may or may not be executed depending on the test
455+
status, this solution guarantees execution immediately after failing `@BeforeAll`,
456+
`@BeforeEach`, `@AfterEach` or `@AfterAll`.
457+
458+
[source,java,indent=0]
459+
.An exception handling extension that records application state on error
460+
----
461+
include::{testDir}/example/exception/RecordStateOnErrorExtension.java[tags=user_guide]
462+
----
463+
464+
Multiple execution exception handlers may be invoked for the same lifecycle method in
465+
order of declaration. If one of the handlers swallows the handled exception, subsequent
466+
ones will not be executed, and no failure will be propagated to JUnit engine, as if the
467+
exception was never thrown. Handlers may also choose to rethrow the exception or throw
468+
a different one, potentially wrapping the original.
469+
470+
Extensions implementing `{LifecycleMethodExecutionExceptionHandler}` that wish to handle
471+
exceptions thrown during `@BeforeAll` or `@AfterAll` need to be registered on a class level,
472+
while handlers for `BeforeEach` and `AfterEach` may be also registered for individual
473+
test methods.
474+
475+
[source,java,indent=0]
476+
.An exception handling extension that records application state on error
477+
----
478+
include::{testDir}/example/exception/MultipleHandlersTestCase.java[tags=user_guide]
479+
----
480+
448481
[[extensions-intercepting-invocations]]
449482
=== Intercepting Invocations
450483

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

587-
The following table further explains the twelve steps in the
620+
The following table further explains the sixteen steps in the
588621
<<extensions-execution-order-diagram>> diagram.
589622

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

602635
| 3
636+
| interface `org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler
637+
#handleBeforeAllMethodExecutionException`
638+
| extension code for handling exceptions thrown during a method annotated with `@BeforeAll`
639+
640+
| 4
603641
| interface `org.junit.jupiter.api.extension.BeforeEachCallback`
604642
| extension code executed before each test is executed
605643

606-
| 4
644+
| 5
607645
| annotation `org.junit.jupiter.api.BeforeEach`
608646
| user code executed before each test is executed
609647

610-
| 5
648+
| 6
649+
| interface `org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler
650+
#handleBeforeEachMethodExecutionException`
651+
| extension code for handling exceptions thrown during a method annotated with `@BeforeEach`
652+
653+
| 7
611654
| interface `org.junit.jupiter.api.extension.BeforeTestExecutionCallback`
612655
| extension code executed immediately before a test is executed
613656

614-
| 6
657+
| 8
615658
| annotation `org.junit.jupiter.api.Test`
616659
| user code of the actual test method
617660

618-
| 7
661+
| 9
619662
| interface `org.junit.jupiter.api.extension.TestExecutionExceptionHandler`
620663
| extension code for handling exceptions thrown during a test
621664

622-
| 8
665+
| 10
623666
| interface `org.junit.jupiter.api.extension.AfterTestExecutionCallback`
624667
| extension code executed immediately after test execution and its corresponding exception handlers
625668

626-
| 9
669+
| 11
627670
| annotation `org.junit.jupiter.api.AfterEach`
628671
| user code executed after each test is executed
629672

630-
| 10
673+
| 12
674+
| interface `org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler
675+
#handleAfterEachMethodExecutionException`
676+
| extension code for handling exceptions thrown during a method annotated with `@AfterEach`
677+
678+
| 13
631679
| interface `org.junit.jupiter.api.extension.AfterEachCallback`
632680
| extension code executed after each test is executed
633681

634-
| 11
682+
| 14
635683
| annotation `org.junit.jupiter.api.AfterAll`
636684
| user code executed after all tests of the container are executed
637685

638-
| 12
686+
| 15
687+
| interface `org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler
688+
#handleAfterAllMethodExecutionException`
689+
| extension code for handling exceptions thrown during a method annotated with `@AfterAll`
690+
691+
| 16
639692
| interface `org.junit.jupiter.api.extension.AfterAllCallback`
640693
| extension code executed after all tests of the container are executed
641694

642695
|===
643696

644-
In the simplest case only the actual test method will be executed (step 6); all other
697+
In the simplest case only the actual test method will be executed (step 8); all other
645698
steps are optional depending on the presence of user code or extension support for the
646699
corresponding lifecycle callback. For further details on the various lifecycle callbacks
647700
please consult the respective Javadoc for each annotation and extension.
-77.8 KB
Loading
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2015-2019 the original author or authors.
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License v2.0 which
6+
* accompanies this distribution and is available at
7+
*
8+
* https://www.eclipse.org/legal/epl-v20.html
9+
*/
10+
11+
package example.exception;
12+
13+
import static example.exception.MultipleHandlersTestCase.ThirdExecutedHandler;
14+
15+
import org.junit.jupiter.api.Test;
16+
import org.junit.jupiter.api.extension.ExtendWith;
17+
import org.junit.jupiter.api.extension.ExtensionContext;
18+
import org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler;
19+
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
20+
21+
// @formatter:off
22+
// tag::user_guide[]
23+
// Register handlers for @Test, @BeforeEach, @AfterEach as well as @BeforeAll and @AfterAll
24+
@ExtendWith({ThirdExecutedHandler.class})
25+
class MultipleHandlersTestCase {
26+
27+
// Register handlers for @Test, @BeforeEach, @AfterEach only
28+
@ExtendWith(SecondExecutedHandler.class)
29+
@ExtendWith(FirstExecutedHandler.class)
30+
@Test
31+
void testMethod() {
32+
}
33+
34+
// end::user_guide[]
35+
36+
static class FirstExecutedHandler implements TestExecutionExceptionHandler {
37+
@Override
38+
public void handleTestExecutionException(ExtensionContext context, Throwable ex)
39+
throws Throwable {
40+
throw ex;
41+
}
42+
}
43+
44+
static class SecondExecutedHandler implements LifecycleMethodExecutionExceptionHandler {
45+
@Override
46+
public void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable ex)
47+
throws Throwable {
48+
throw ex;
49+
}
50+
}
51+
52+
static class ThirdExecutedHandler implements LifecycleMethodExecutionExceptionHandler {
53+
@Override
54+
public void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable ex)
55+
throws Throwable {
56+
throw ex;
57+
}
58+
}
59+
}
60+
// @formatter:on
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2015-2019 the original author or authors.
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License v2.0 which
6+
* accompanies this distribution and is available at
7+
*
8+
* https://www.eclipse.org/legal/epl-v20.html
9+
*/
10+
11+
package example.exception;
12+
13+
import org.junit.jupiter.api.extension.ExtensionContext;
14+
import org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler;
15+
16+
// @formatter:off
17+
// tag::user_guide[]
18+
class RecordStateOnErrorExtension implements LifecycleMethodExecutionExceptionHandler {
19+
20+
@Override
21+
public void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable ex)
22+
throws Throwable {
23+
memoryDumpForFurtherInvestigation("Failure recorded during class setup");
24+
throw ex;
25+
}
26+
27+
@Override
28+
public void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable ex)
29+
throws Throwable {
30+
memoryDumpForFurtherInvestigation("Failure recorded during test setup");
31+
throw ex;
32+
}
33+
34+
@Override
35+
public void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable ex)
36+
throws Throwable {
37+
memoryDumpForFurtherInvestigation("Failure recorded during test cleanup");
38+
throw ex;
39+
}
40+
41+
@Override
42+
public void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable ex)
43+
throws Throwable {
44+
memoryDumpForFurtherInvestigation("Failure recorded during class cleanup");
45+
throw ex;
46+
}
47+
// end::user_guide[]
48+
49+
private void memoryDumpForFurtherInvestigation(String error) {
50+
51+
}
52+
}
53+
// @formatter:on

0 commit comments

Comments
 (0)