Skip to content

Commit d72090c

Browse files
committed
Merge branch 'main' into feature/2816
2 parents 6b4ee03 + c5359ec commit d72090c

File tree

79 files changed

+4705
-409
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+4705
-409
lines changed

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

+3
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ endif::[]
111111
:ClassOrderer_OrderAnnotation: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.OrderAnnotation.html[ClassOrderer.OrderAnnotation]
112112
:ClassOrderer_Random: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.Random.html[ClassOrderer.Random]
113113
:ClassOrderer: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.html[ClassOrderer]
114+
:ContainerTemplate: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ContainerTemplate.html[@ContainerTemplate]
114115
:Disabled: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Disabled.html[@Disabled]
115116
:MethodOrderer_Alphanumeric: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.Alphanumeric.html[MethodOrderer.Alphanumeric]
116117
:MethodOrderer_DisplayName: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.DisplayName.html[MethodOrderer.DisplayName]
@@ -142,6 +143,8 @@ endif::[]
142143
:BeforeAllCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeAllCallback.html[BeforeAllCallback]
143144
:BeforeEachCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeEachCallback.html[BeforeEachCallback]
144145
:BeforeTestExecutionCallback: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeTestExecutionCallback.html[BeforeTestExecutionCallback]
146+
:ContainerTemplateInvocationContext: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ContainerTemplateInvocationContext.html[ContainerTemplateInvocationContext]
147+
:ContainerTemplateInvocationContextProvider: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ContainerTemplateInvocationContextProvider.html[ContainerTemplateInvocationContextProvider]
145148
:ExecutableInvoker: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExecutableInvoker.html[ExecutableInvoker]
146149
:ExecutionCondition: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExecutionCondition.html[ExecutionCondition]
147150
:ExtendWith: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtendWith.html[@ExtendWith]

documentation/src/docs/asciidoc/release-notes/release-notes-5.13.0-M1.adoc

+16-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,13 @@ repository on GitHub.
3535
[[release-notes-5.13.0-M1-junit-jupiter-bug-fixes]]
3636
==== Bug Fixes
3737

38-
* ❓
38+
* If `@ParameterizedTest(autoCloseArguments = true)`, all arguments returned by the used
39+
`ArgumentsProvider` implementations are now closed even if the test method declares
40+
fewer parameters.
41+
* `AutoCloseable` arguments returned by an `ArgumentsProvider` are now closed even if they
42+
are wrapped with `Named`.
43+
* `AutoCloseable` arguments returned by an `ArgumentsProvider` are now closed even if a
44+
failure happens prior to invoking the parameterized method.
3945

4046
[[release-notes-5.13.0-M1-junit-jupiter-deprecations-and-breaking-changes]]
4147
==== Deprecations and Breaking Changes
@@ -45,7 +51,15 @@ repository on GitHub.
4551
[[release-notes-5.13.0-M1-junit-jupiter-new-features-and-improvements]]
4652
==== New Features and Improvements
4753

48-
* ❓
54+
* Introduce `@ContainerTemplate` and `ContainerTemplateInvocationContextProvider` that
55+
allow declaring a top-level or `@Nested` test class as a template to be invoked multiple
56+
times. This may be used, for example, to inject different parameters to be used by all
57+
tests in the container template class or to set up each invocation of the container
58+
template differently.
59+
* New `TestTemplateInvocationContext.prepareInvocation(ExtensionContext)` callback method
60+
allows preparing the `ExtensionContext` before the test template method is invoked. This
61+
may be used, for example, to store entries in its `Store` to benefit from its cleanup
62+
support or for retrieval by other extensions.
4963

5064

5165
[[release-notes-5.13.0-M1-junit-vintage]]

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

+40
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,46 @@ You may override the `getTestInstantiationExtensionContextScope(...)` method to
765765
on the test method level.
766766
====
767767

768+
[[extensions-container-templates]]
769+
=== Providing Invocation Contexts for Container Templates
770+
771+
A `{ContainerTemplate}` class can only be executed when at least one
772+
`{ContainerTemplateInvocationContextProvider}` is registered. Each such provider is
773+
responsible for providing a `Stream` of `{ContainerTemplateInvocationContext}` instances.
774+
Each context may specify a custom display name and a list of additional extensions that
775+
will only be used for the next invocation of the `{ContainerTemplate}` class.
776+
777+
The following example shows how to write a container template as well as how to register
778+
and implement a `{ContainerTemplateInvocationContextProvider}`.
779+
780+
[source,java,indent=0]
781+
.A container template with accompanying extension
782+
----
783+
include::{testDir}/example/ContainerTemplateDemo.java[tags=user_guide]
784+
----
785+
786+
In this example, the container template will be invoked twice, meaning all test methods in
787+
the container template class will be executed twice. The display names of the container
788+
invocations will be `apple` and `banana` as specified by the invocation context. Each
789+
invocation registers a custom `{TestInstancePostProcessor}` which is used to inject a
790+
value into a field. The output when using the `ConsoleLauncher` is as follows.
791+
792+
....
793+
└─ ContainerTemplateDemo ✔
794+
├─ apple ✔
795+
│ ├─ notNull() ✔
796+
│ └─ wellKnown() ✔
797+
└─ banana ✔
798+
├─ notNull() ✔
799+
└─ wellKnown() ✔
800+
....
801+
802+
The `{ContainerTemplateInvocationContextProvider}` extension API is primarily intended for
803+
implementing different kinds of tests that rely on repetitive invocation of _all_ test
804+
methods in a test class albeit in different contexts — for example, with different
805+
parameters, by preparing the test class instance differently, or multiple times without
806+
modifying the context.
807+
768808
[[extensions-test-templates]]
769809
=== Providing Invocation Contexts for Test Templates
770810

documentation/src/docs/asciidoc/user-guide/writing-tests.adoc

+14-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ in the `junit-jupiter-api` module.
3232
| `@ParameterizedTest` | Denotes that a method is a <<writing-tests-parameterized-tests, parameterized test>>. Such methods are inherited unless they are overridden.
3333
| `@RepeatedTest` | Denotes that a method is a test template for a <<writing-tests-repeated-tests, repeated test>>. Such methods are inherited unless they are overridden.
3434
| `@TestFactory` | Denotes that a method is a test factory for <<writing-tests-dynamic-tests, dynamic tests>>. Such methods are inherited unless they are overridden.
35-
| `@TestTemplate` | Denotes that a method is a <<writing-tests-test-templates, template for test cases>> designed to be invoked multiple times depending on the number of invocation contexts returned by the registered <<extensions-test-templates, providers>>. Such methods are inherited unless they are overridden.
35+
| `@TestTemplate` | Denotes that a method is a <<writing-tests-test-templates, template for a test case>> designed to be invoked multiple times depending on the number of invocation contexts returned by the registered <<extensions-test-templates, providers>>. Such methods are inherited unless they are overridden.
3636
| `@TestClassOrder` | Used to configure the <<writing-tests-test-execution-order-classes, test class execution order>> for `@Nested` test classes in the annotated test class. Such annotations are inherited.
3737
| `@TestMethodOrder` | Used to configure the <<writing-tests-test-execution-order-methods, test method execution order>> for the annotated test class; similar to JUnit 4's `@FixMethodOrder`. Such annotations are inherited.
3838
| `@TestInstance` | Used to configure the <<writing-tests-test-instance-lifecycle, test instance lifecycle>> for the annotated test class. Such annotations are inherited.
@@ -42,6 +42,7 @@ in the `junit-jupiter-api` module.
4242
| `@AfterEach` | Denotes that the annotated method should be executed _after_ *each* `@Test`, `@RepeatedTest`, `@ParameterizedTest`, or `@TestFactory` method in the current class; analogous to JUnit 4's `@After`. Such methods are inherited unless they are overridden.
4343
| `@BeforeAll` | Denotes that the annotated method should be executed _before_ *all* `@Test`, `@RepeatedTest`, `@ParameterizedTest`, and `@TestFactory` methods in the current class; analogous to JUnit 4's `@BeforeClass`. Such methods are inherited unless they are overridden and must be `static` unless the "per-class" <<writing-tests-test-instance-lifecycle, test instance lifecycle>> is used.
4444
| `@AfterAll` | Denotes that the annotated method should be executed _after_ *all* `@Test`, `@RepeatedTest`, `@ParameterizedTest`, and `@TestFactory` methods in the current class; analogous to JUnit 4's `@AfterClass`. Such methods are inherited unless they are overridden and must be `static` unless the "per-class" <<writing-tests-test-instance-lifecycle, test instance lifecycle>> is used.
45+
| `@ContainerTemplate` | Denotes that the annotated class is a <<writing-tests-container-templates, template for a set of test cases>> designed to be executed multiple times depending on the number of invocation contexts returned by the registered <<extensions-container-templates, providers>>.
4546
| `@Nested` | Denotes that the annotated class is a non-static <<writing-tests-nested,nested test class>>. On Java 8 through Java 15, `@BeforeAll` and `@AfterAll` methods cannot be used directly in a `@Nested` test class unless the "per-class" <<writing-tests-test-instance-lifecycle, test instance lifecycle>> is used. Beginning with Java 16, `@BeforeAll` and `@AfterAll` methods can be declared as `static` in a `@Nested` test class with either test instance lifecycle mode. Such annotations are not inherited.
4647
| `@Tag` | Used to declare <<writing-tests-tagging-and-filtering,tags for filtering tests>>, either at the class or method level; analogous to test groups in TestNG or Categories in JUnit 4. Such annotations are inherited at the class level but not at the method level.
4748
| `@Disabled` | Used to <<writing-tests-disabling,disable>> a test class or test method; analogous to JUnit 4's `@Ignore`. Such annotations are not inherited.
@@ -2448,12 +2449,22 @@ lifecycle methods (e.g. `@BeforeEach`) and test class constructors.
24482449
include::{testDir}/example/ParameterizedTestDemo.java[tags=ParameterResolver_example]
24492450
----
24502451

2452+
[[writing-tests-container-templates]]
2453+
=== Container Templates
2454+
2455+
A `{ContainerTemplate}` class is not a regular test class but rather a template for the
2456+
contained test cases. As such, it is designed to be invoked multiple times depending on
2457+
invocation contexts returned by the registered providers. Thus, it must be used in
2458+
conjunction with a registered `{ContainerTemplateInvocationContextProvider}` extension.
2459+
Each invocation of a container template class behaves like the execution of a regular test
2460+
class with full support for the same lifecycle callbacks and extensions. Please refer to
2461+
<<extensions-container-templates>> for usage examples.
24512462

24522463
[[writing-tests-test-templates]]
24532464
=== Test Templates
24542465

2455-
A `{TestTemplate}` method is not a regular test case but rather a template for test
2456-
cases. As such, it is designed to be invoked multiple times depending on the number of
2466+
A `{TestTemplate}` method is not a regular test case but rather a template for a test
2467+
case. As such, it is designed to be invoked multiple times depending on the number of
24572468
invocation contexts returned by the registered providers. Thus, it must be used in
24582469
conjunction with a registered `{TestTemplateInvocationContextProvider}` extension. Each
24592470
invocation of a test template method behaves like the execution of a regular `@Test`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright 2015-2025 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;
12+
13+
import static java.util.Collections.singletonList;
14+
import static java.util.Collections.unmodifiableList;
15+
import static org.junit.jupiter.api.Assertions.assertNotNull;
16+
import static org.junit.jupiter.api.Assertions.assertTrue;
17+
18+
import java.util.Arrays;
19+
import java.util.List;
20+
import java.util.stream.Stream;
21+
22+
import example.ContainerTemplateDemo.MyContainerTemplateInvocationContextProvider;
23+
24+
import org.junit.jupiter.api.ContainerTemplate;
25+
import org.junit.jupiter.api.Test;
26+
import org.junit.jupiter.api.extension.ContainerTemplateInvocationContext;
27+
import org.junit.jupiter.api.extension.ContainerTemplateInvocationContextProvider;
28+
import org.junit.jupiter.api.extension.ExtendWith;
29+
import org.junit.jupiter.api.extension.Extension;
30+
import org.junit.jupiter.api.extension.ExtensionContext;
31+
import org.junit.jupiter.api.extension.TestInstancePostProcessor;
32+
33+
// tag::user_guide[]
34+
@ContainerTemplate
35+
@ExtendWith(MyContainerTemplateInvocationContextProvider.class)
36+
class ContainerTemplateDemo {
37+
38+
static final List<String> WELL_KNOWN_FRUITS
39+
// tag::custom_line_break[]
40+
= unmodifiableList(Arrays.asList("apple", "banana", "lemon"));
41+
42+
private String fruit;
43+
44+
@Test
45+
void notNull() {
46+
assertNotNull(fruit);
47+
}
48+
49+
@Test
50+
void wellKnown() {
51+
assertTrue(WELL_KNOWN_FRUITS.contains(fruit));
52+
}
53+
54+
// end::user_guide[]
55+
static
56+
// tag::user_guide[]
57+
public class MyContainerTemplateInvocationContextProvider
58+
// tag::custom_line_break[]
59+
implements ContainerTemplateInvocationContextProvider {
60+
61+
@Override
62+
public boolean supportsContainerTemplate(ExtensionContext context) {
63+
return true;
64+
}
65+
66+
@Override
67+
public Stream<ContainerTemplateInvocationContext>
68+
// tag::custom_line_break[]
69+
provideContainerTemplateInvocationContexts(ExtensionContext context) {
70+
71+
return Stream.of(invocationContext("apple"), invocationContext("banana"));
72+
}
73+
74+
private ContainerTemplateInvocationContext invocationContext(String parameter) {
75+
return new ContainerTemplateInvocationContext() {
76+
@Override
77+
public String getDisplayName(int invocationIndex) {
78+
return parameter;
79+
}
80+
81+
// end::user_guide[]
82+
@SuppressWarnings("Convert2Lambda")
83+
// tag::user_guide[]
84+
@Override
85+
public List<Extension> getAdditionalExtensions() {
86+
return singletonList(new TestInstancePostProcessor() {
87+
@Override
88+
public void postProcessTestInstance(
89+
// tag::custom_line_break[]
90+
Object testInstance, ExtensionContext context) {
91+
((ContainerTemplateDemo) testInstance).fruit = parameter;
92+
}
93+
});
94+
}
95+
};
96+
}
97+
}
98+
}
99+
// end::user_guide[]

gradle/libs.versions.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ log4j = "2.24.3"
1717
logback = "1.5.16"
1818
mockito = "5.15.2"
1919
opentest4j = "1.3.0"
20-
openTestReporting = "0.2.0-M3"
20+
openTestReporting = "0.2.0"
2121
snapshotTests = "1.11.0"
2222
surefire = "3.5.2"
2323
xmlunit = "2.10.0"
@@ -94,7 +94,7 @@ asciidoctorPdf = { id = "org.asciidoctor.jvm.pdf", version.ref = "asciidoctor-pl
9494
bnd = { id = "biz.aQute.bnd", version.ref = "bnd" }
9595
buildParameters = { id = "org.gradlex.build-parameters", version = "1.4.4" }
9696
commonCustomUserData = { id = "com.gradle.common-custom-user-data-gradle-plugin", version = "2.1" }
97-
develocity = { id = "com.gradle.develocity", version = "3.19.1" }
97+
develocity = { id = "com.gradle.develocity", version = "3.19.2" }
9898
foojayResolver = { id = "org.gradle.toolchains.foojay-resolver", version = "0.9.0" }
9999
gitPublish = { id = "org.ajoberstar.git-publish", version = "5.1.0" }
100100
jmh = { id = "me.champeau.jmh", version = "0.7.3" }

junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterAll.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,13 @@
2525
* executed <em>after</em> <strong>all</strong> tests in the current test class.
2626
*
2727
* <p>In contrast to {@link AfterEach @AfterEach} methods, {@code @AfterAll}
28-
* methods are only executed once for a given test class.
28+
* methods are only executed once per execution of a given test class. If the
29+
* test class is annotated with {@link ContainerTemplate @ContainerTemplate},
30+
* the {@code @AfterAll} methods are executed once after the last invocation of
31+
* the container template. If a {@link Nested @Nested} test class is declared in
32+
* a {@link ContainerTemplate @ContainerTemplate} class, its {@code @AfterAll}
33+
* methods are called once per execution of the nested test class, namely, once
34+
* per invocation of the outer container template.
2935
*
3036
* <h2>Method Signatures</h2>
3137
*

junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeAll.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,13 @@
2525
* executed <em>before</em> <strong>all</strong> tests in the current test class.
2626
*
2727
* <p>In contrast to {@link BeforeEach @BeforeEach} methods, {@code @BeforeAll}
28-
* methods are only executed once for a given test class.
28+
* methods are only executed once per execution of a given test class. If the
29+
* test class is annotated with {@link ContainerTemplate @ContainerTemplate},
30+
* the {@code @BeforeAll} methods are executed once before the first invocation
31+
* of the container template. If a {@link Nested @Nested} test class is declared
32+
* in a {@link ContainerTemplate @ContainerTemplate} class, its
33+
* {@code @BeforeAll} methods are called once per execution of the nested test
34+
* class, namely, once per invocation of the outer container template.
2935
*
3036
* <h2>Method Signatures</h2>
3137
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2015-2025 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 org.junit.jupiter.api;
12+
13+
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
14+
15+
import java.lang.annotation.Documented;
16+
import java.lang.annotation.ElementType;
17+
import java.lang.annotation.Retention;
18+
import java.lang.annotation.RetentionPolicy;
19+
import java.lang.annotation.Target;
20+
21+
import org.apiguardian.api.API;
22+
import org.junit.platform.commons.annotation.Testable;
23+
24+
/**
25+
* {@code @ContainerTemplate} is used to signal that the annotated class is a
26+
* <em>container template</em>.
27+
*
28+
* <p>In contrast to regular test classes, a container template is not directly
29+
* a test class but rather a template for a set of test cases. As such, it is
30+
* designed to be invoked multiple times depending on the number of {@linkplain
31+
* org.junit.jupiter.api.extension.ContainerTemplateInvocationContext invocation
32+
* contexts} returned by the registered {@linkplain
33+
* org.junit.jupiter.api.extension.ContainerTemplateInvocationContextProvider
34+
* providers}. Must be used together with at least one provider. Otherwise,
35+
* execution will fail.
36+
*
37+
* <p>Each invocation of a container template method behaves like the execution
38+
* of a regular test class with full support for the same lifecycle callbacks
39+
* and extensions.
40+
*
41+
* <p>{@code @ContainerTemplate} may be combined with {@link Nested @Nested} and
42+
* a container template may contain regular nested test classes or nested
43+
* container templates.
44+
*
45+
* <p>{@code @ContainerTemplate} may also be used as a meta-annotation in order
46+
* to create a custom <em>composed annotation</em> that inherits the semantics
47+
* of {@code @ContainerTemplate}.
48+
*
49+
* <h2>Inheritance</h2>
50+
*
51+
* <p>The {@code @ContainerTemplate} annotation is not inherited to subclasses
52+
* but needs to be declared on each container template class individually.
53+
*
54+
* @since 5.13
55+
* @see TestTemplate
56+
* @see org.junit.jupiter.api.extension.ContainerTemplateInvocationContext
57+
* @see org.junit.jupiter.api.extension.ContainerTemplateInvocationContextProvider
58+
*/
59+
@Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE })
60+
@Retention(RetentionPolicy.RUNTIME)
61+
@Documented
62+
@API(status = EXPERIMENTAL, since = "5.13")
63+
@Testable
64+
public @interface ContainerTemplate {
65+
}

0 commit comments

Comments
 (0)