Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce per-invocation lifecycle callbacks for container templates #4353

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions documentation/src/docs/asciidoc/link-attributes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,12 @@ endif::[]
// Jupiter Extension APIs
:extension-api-package: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/package-summary.html[org.junit.jupiter.api.extension]
:AfterAllCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/AfterAllCallback.html[AfterAllCallback]
:AfterContainerTemplateInvocationCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/AfterContainerTemplateInvocationCallback.html[AfterContainerTemplateInvocationCallback]
:AfterEachCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/AfterEachCallback.html[AfterEachCallback]
:AfterTestExecutionCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/AfterTestExecutionCallback.html[AfterTestExecutionCallback]
:ParameterContext: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ParameterContext.html[ParameterContext]
:BeforeAllCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeAllCallback.html[BeforeAllCallback]
:BeforeContainerTemplateInvocationCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeContainerTemplateInvocationCallback.html[BeforeContainerTemplateInvocationCallback]
:BeforeEachCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeEachCallback.html[BeforeEachCallback]
:BeforeTestExecutionCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeTestExecutionCallback.html[BeforeTestExecutionCallback]
:ContainerTemplateInvocationContext: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ContainerTemplateInvocationContext.html[ContainerTemplateInvocationContext]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ repository on GitHub.
updated documentation in the
<<../user-guide/index.adoc#writing-tests-display-name-generator, User Guide>> for an
example.
* New `BeforeContainerTemplateInvocationCallback` and
`AfterContainerTemplateInvocationCallback` extension callback interfaces allow
implementing extensions that are invoked before and after each invocation of a container
template.
* New `TestTemplateInvocationContext.prepareInvocation(ExtensionContext)` callback method
which allows extensions to prepare the `ExtensionContext` before the test template
method is invoked. This may be used, for example, to store entries in the
Expand Down
130 changes: 51 additions & 79 deletions documentation/src/docs/asciidoc/user-guide/extensions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -636,10 +636,14 @@ test execution lifecycle. Consult the following sections for examples and the Ja
each of these interfaces in the `{extension-api-package}` package for further details.

* `{BeforeAllCallback}`
** `{BeforeEachCallback}`
*** `{BeforeTestExecutionCallback}`
*** `{AfterTestExecutionCallback}`
** `{AfterEachCallback}`
** `{BeforeContainerTemplateInvocationCallback}` (only applicable for
<<writing-tests-container-templates, container templates>>)
*** `{BeforeEachCallback}`
**** `{BeforeTestExecutionCallback}`
**** `{AfterTestExecutionCallback}`
*** `{AfterEachCallback}`
** `{AfterContainerTemplateInvocationCallback}` (only applicable for
<<writing-tests-container-templates, container templates>>)
* `{AfterAllCallback}`

.Implementing Multiple Extension APIs
Expand Down Expand Up @@ -1010,81 +1014,48 @@ image::extensions_lifecycle.png[caption='',title='{figure-caption}']
The following table further explains the sixteen steps in the
<<extensions-execution-order-diagram>> diagram.

[cols="5,15,80"]
|===
| Step | Interface/Annotation | Description

| 1
| interface `org.junit.jupiter.api.extension.BeforeAllCallback`
| extension code executed before all tests of the container are executed

| 2
| annotation `org.junit.jupiter.api.BeforeAll`
| 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 from `@BeforeAll` methods

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

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

| 6
| interface `org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler
#handleBeforeEachMethodExecutionException`
| extension code for handling exceptions thrown from `@BeforeEach` methods

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

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

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

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

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

| 12
| interface `org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler
#handleAfterEachMethodExecutionException`
| extension code for handling exceptions thrown from `@AfterEach` methods

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

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

| 15
| interface `org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler
#handleAfterAllMethodExecutionException`
| extension code for handling exceptions thrown from `@AfterAll` methods

| 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 8); all other
. *interface* `*org.junit.jupiter.api.extension.BeforeAllCallback*` +
extension code executed before all tests of the container are executed
. *annotation* `*org.junit.jupiter.api.BeforeAll*` +
user code executed before all tests of the container are executed
. *interface* `*org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler
#handleBeforeAllMethodExecutionException*` +
extension code for handling exceptions thrown from `@BeforeAll` methods
. *interface* `*org.junit.jupiter.api.extension.BeforeContainerTemplateInvocationCallback*` +
extension code executed before each container template invocation is executed (only applicable if the test class is a <<writing-tests-container-templates, container template>>)
. *interface* `*org.junit.jupiter.api.extension.BeforeEachCallback*` +
extension code executed before each test is executed
. *annotation* `*org.junit.jupiter.api.BeforeEach*` +
user code executed before each test is executed
. *interface* `*org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler
#handleBeforeEachMethodExecutionException*` +
extension code for handling exceptions thrown from `@BeforeEach` methods
. *interface* `*org.junit.jupiter.api.extension.BeforeTestExecutionCallback*` +
extension code executed immediately before a test is executed
. *annotation* `*org.junit.jupiter.api.Test*` +
user code of the actual test method
. *interface* `*org.junit.jupiter.api.extension.TestExecutionExceptionHandler*` +
extension code for handling exceptions thrown during a test
. *interface* `*org.junit.jupiter.api.extension.AfterTestExecutionCallback*` +
extension code executed immediately after test execution and its corresponding exception handlers
. *annotation* `*org.junit.jupiter.api.AfterEach*` +
user code executed after each test is executed
. *interface* `*org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler
#handleAfterEachMethodExecutionException*` +
extension code for handling exceptions thrown from `@AfterEach` methods
. *interface* `*org.junit.jupiter.api.extension.AfterEachCallback*` +
extension code executed after each test is executed
. *interface* `*org.junit.jupiter.api.extension.AfterContainerTemplateInvocationCallback*` +
extension code executed after each container template invocation is executed (only applicable if the test class is a <<writing-tests-container-templates, container template>>)
. *annotation* `*org.junit.jupiter.api.AfterAll*` +
user code executed after all tests of the container are executed
. *interface* `*org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler
#handleAfterAllMethodExecutionException*` +
extension code for handling exceptions thrown from `@AfterAll` methods
. *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 9); 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 All @@ -1097,6 +1068,7 @@ by implementing <<extensions-intercepting-invocations, `InvocationInterceptor`>>

JUnit Jupiter always guarantees _wrapping_ behavior for multiple registered extensions
that implement lifecycle callbacks such as `BeforeAllCallback`, `AfterAllCallback`,
`BeforeContainerTemplateInvocationCallback`, `AfterContainerTemplateInvocationCallback`,
`BeforeEachCallback`, `AfterEachCallback`, `BeforeTestExecutionCallback`, and
`AfterTestExecutionCallback`.

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
* @see TestTemplate
* @see org.junit.jupiter.api.extension.ContainerTemplateInvocationContext
* @see org.junit.jupiter.api.extension.ContainerTemplateInvocationContextProvider
* @see org.junit.jupiter.api.extension.BeforeContainerTemplateInvocationCallback
* @see org.junit.jupiter.api.extension.AfterContainerTemplateInvocationCallback
*/
@Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@
* <p>JUnit Jupiter guarantees <em>wrapping behavior</em> for multiple
* registered extensions that implement lifecycle callbacks such as
* {@link BeforeAllCallback}, {@link AfterAllCallback},
* {@link BeforeEachCallback}, {@link AfterEachCallback},
* {@link BeforeTestExecutionCallback}, and {@link AfterTestExecutionCallback}.
* {@link BeforeContainerTemplateInvocationCallback},
* {@link AfterContainerTemplateInvocationCallback}, {@link BeforeEachCallback},
* {@link AfterEachCallback}, {@link BeforeTestExecutionCallback}, and
* {@link AfterTestExecutionCallback}.
*
* <p>That means that, given two extensions {@code Extension1} and
* {@code Extension2} with {@code Extension1} registered before
Expand All @@ -54,6 +56,8 @@
* @see AfterEachCallback
* @see BeforeTestExecutionCallback
* @see AfterTestExecutionCallback
* @see BeforeContainerTemplateInvocationCallback
* @see AfterContainerTemplateInvocationCallback
*/
@FunctionalInterface
@API(status = STABLE, since = "5.0")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2015-2025 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 org.junit.jupiter.api.extension;

import static org.apiguardian.api.API.Status.EXPERIMENTAL;

import org.apiguardian.api.API;

/**
* {@code AfterContainerTemplateInvocationCallback} defines the API for
* {@link Extension Extensions} that wish to provide additional behavior
* <strong>once</strong> before each invocation of a
* {@linkplain org.junit.jupiter.api.ContainerTemplate container template}.
*
* <p>Concrete implementations often implement
* {@link BeforeContainerTemplateInvocationCallback}
* as well.
*
* <h2>Constructor Requirements</h2>
*
* <p>Consult the documentation in {@link Extension} for details on
* constructor requirements.
*
* <h2>Wrapping Behavior</h2>
*
* <p>JUnit Jupiter guarantees <em>wrapping behavior</em> for multiple
* registered extensions that implement lifecycle callbacks such as
* {@link BeforeAllCallback}, {@link AfterAllCallback},
* {@link AfterContainerTemplateInvocationCallback},
* {@link AfterContainerTemplateInvocationCallback}, {@link BeforeEachCallback},
* {@link AfterEachCallback}, {@link BeforeTestExecutionCallback}, and
* {@link AfterTestExecutionCallback}.
*
* <p>That means that, given two extensions {@code Extension1} and
* {@code Extension2} with {@code Extension1} registered before
* {@code Extension2}, any "before" callbacks implemented by {@code Extension1}
* are guaranteed to execute before any "before" callbacks implemented by
* {@code Extension2}. Similarly, given the two same two extensions registered
* in the same order, any "after" callbacks implemented by {@code Extension1}
* are guaranteed to execute after any "after" callbacks implemented by
* {@code Extension2}. {@code Extension1} is therefore said to <em>wrap</em>
* {@code Extension2}.
*
* @since 5.13
* @see org.junit.jupiter.api.ContainerTemplate
* @see BeforeContainerTemplateInvocationCallback
* @see BeforeAllCallback
* @see AfterAllCallback
* @see BeforeEachCallback
* @see AfterEachCallback
* @see BeforeTestExecutionCallback
* @see AfterTestExecutionCallback
*/
@FunctionalInterface
@API(status = EXPERIMENTAL, since = "5.13")
public interface AfterContainerTemplateInvocationCallback extends Extension {

/**
* Callback that is invoked <em>after</em> each invocation of a container
* template.
*
* @param context the current extension context; never {@code null}
*/
void afterContainerTemplateInvocation(ExtensionContext context) throws Exception;

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@
* <p>JUnit Jupiter guarantees <em>wrapping behavior</em> for multiple
* registered extensions that implement lifecycle callbacks such as
* {@link BeforeAllCallback}, {@link AfterAllCallback},
* {@link BeforeEachCallback}, {@link AfterEachCallback},
* {@link BeforeTestExecutionCallback}, and {@link AfterTestExecutionCallback}.
* {@link BeforeContainerTemplateInvocationCallback},
* {@link AfterContainerTemplateInvocationCallback}, {@link BeforeEachCallback},
* {@link AfterEachCallback}, {@link BeforeTestExecutionCallback}, and
* {@link AfterTestExecutionCallback}.
*
* <p>That means that, given two extensions {@code Extension1} and
* {@code Extension2} with {@code Extension1} registered before
Expand All @@ -57,6 +59,8 @@
* @see AfterTestExecutionCallback
* @see BeforeAllCallback
* @see AfterAllCallback
* @see BeforeContainerTemplateInvocationCallback
* @see AfterContainerTemplateInvocationCallback
*/
@FunctionalInterface
@API(status = STABLE, since = "5.0")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@
* <p>JUnit Jupiter guarantees <em>wrapping behavior</em> for multiple
* registered extensions that implement lifecycle callbacks such as
* {@link BeforeAllCallback}, {@link AfterAllCallback},
* {@link BeforeEachCallback}, {@link AfterEachCallback},
* {@link BeforeTestExecutionCallback}, and {@link AfterTestExecutionCallback}.
* {@link BeforeContainerTemplateInvocationCallback},
* {@link AfterContainerTemplateInvocationCallback}, {@link BeforeEachCallback},
* {@link AfterEachCallback}, {@link BeforeTestExecutionCallback}, and
* {@link AfterTestExecutionCallback}.
*
* <p>That means that, given two extensions {@code Extension1} and
* {@code Extension2} with {@code Extension1} registered before
Expand All @@ -58,6 +60,8 @@
* @see AfterEachCallback
* @see BeforeAllCallback
* @see AfterAllCallback
* @see BeforeContainerTemplateInvocationCallback
* @see AfterContainerTemplateInvocationCallback
*/
@FunctionalInterface
@API(status = STABLE, since = "5.0")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@
* <p>JUnit Jupiter guarantees <em>wrapping behavior</em> for multiple
* registered extensions that implement lifecycle callbacks such as
* {@link BeforeAllCallback}, {@link AfterAllCallback},
* {@link BeforeEachCallback}, {@link AfterEachCallback},
* {@link BeforeTestExecutionCallback}, and {@link AfterTestExecutionCallback}.
* {@link BeforeContainerTemplateInvocationCallback},
* {@link AfterContainerTemplateInvocationCallback}, {@link BeforeEachCallback},
* {@link AfterEachCallback}, {@link BeforeTestExecutionCallback}, and
* {@link AfterTestExecutionCallback}.
*
* <p>That means that, given two extensions {@code Extension1} and
* {@code Extension2} with {@code Extension1} registered before
Expand All @@ -54,6 +56,8 @@
* @see AfterEachCallback
* @see BeforeTestExecutionCallback
* @see AfterTestExecutionCallback
* @see BeforeContainerTemplateInvocationCallback
* @see AfterContainerTemplateInvocationCallback
*/
@FunctionalInterface
@API(status = STABLE, since = "5.0")
Expand Down
Loading
Loading