From a08a4d60685a7a8b6fd4a086c2ef64a3cf4a69ed Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Sat, 31 Jan 2026 16:24:29 +0100 Subject: [PATCH] =?UTF-8?q?Document=20@=E2=81=A0SpringExtensionConfig=20in?= =?UTF-8?q?=20the=20reference=20manual?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit also updates the Javadoc for the SpringExtension and @SpringExtensionConfig to point out that the SpringExtension always uses a test-class scoped ExtensionContext if @TestInstance(Lifecycle.PER_CLASS) semantics are in effect. Closes gh-36240 --- .../integration-junit-jupiter.adoc | 42 +++++++++++++++++++ .../support-classes.adoc | 11 +++++ .../junit/jupiter/SpringExtension.java | 7 +++- .../junit/jupiter/SpringExtensionConfig.java | 5 +++ 4 files changed, 64 insertions(+), 1 deletion(-) diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-junit-jupiter.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-junit-jupiter.adoc index 2ac55e12e65..03ea705eaab 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-junit-jupiter.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-junit-jupiter.adoc @@ -5,6 +5,7 @@ The following annotations are supported when used in conjunction with the xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-extension[`SpringExtension`] and JUnit Jupiter (that is, the programming model in JUnit): +* xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-springextensionconfig[`@SpringExtensionConfig`] * xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-junit-jupiter-springjunitconfig[`@SpringJUnitConfig`] * xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-junit-jupiter-springjunitwebconfig[`@SpringJUnitWebConfig`] * xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-testconstructor[`@TestConstructor`] @@ -14,6 +15,47 @@ and JUnit Jupiter (that is, the programming model in JUnit): * xref:testing/annotations/integration-spring/annotation-disabledinaotmode.adoc[`@DisabledInAotMode`] +[[integration-testing-annotations-springextensionconfig]] +== `@SpringExtensionConfig` + +`@SpringExtensionConfig` is a type-level annotation that can be used to configure the +behavior of the `SpringExtension`. + +As of Spring Framework 7.0, the `SpringExtension` is configured to use a test-method +scoped `ExtensionContext`, which enables consistent dependency injection into fields and +constructors from the `ApplicationContext` for the current test method in a `@Nested` +test class hierarchy. However, if a third-party `TestExecutionListener` is not compatible +with the semantics associated with a test-method scoped extension context — or if a +developer wishes to switch to test-class scoped semantics — the `SpringExtension` can be +configured to use a test-class scoped `ExtensionContext` by annotating a top-level test +class with `@SpringExtensionConfig(useTestClassScopedExtensionContext = true)`. + +[TIP] +==== +If your top-level test class is configured to use JUnit Jupiter's +`@TestInstance(Lifecycle.PER_CLASS)` semantics, the `SpringExtension` will always use a +test-class scoped `ExtensionContext`, and there is no need to declare +`@SpringExtensionConfig(useTestClassScopedExtensionContext = true)`. +==== + +[NOTE] +==== +This annotation is currently only applicable to `@Nested` test class hierarchies and +should be applied to the top-level enclosing class of a `@Nested` test class hierarchy. + +Consequently, there is no need to declare this annotation on a test class that does not +contain `@Nested` test classes. +==== + +[NOTE] +==== +xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-nestedtestconfiguration[`@NestedTestConfiguration`] +does not apply to this annotation. + +`@SpringExtensionConfig` will always be detected within a `@Nested` test class hierarchy, +effectively disregarding any `@NestedTestConfiguration(OVERRIDE)` declarations. +==== + [[integration-testing-annotations-junit-jupiter-springjunitconfig]] == `@SpringJUnitConfig` diff --git a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/support-classes.adoc b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/support-classes.adoc index a0e11dfd153..54b9cd7b5cf 100644 --- a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/support-classes.adoc +++ b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/support-classes.adoc @@ -392,6 +392,17 @@ any of its subclasses and nested classes. Thus, you may annotate a top-level tes with `@NestedTestConfiguration`, and that will apply to all of its nested test classes recursively. +[NOTE] +==== +As of Spring Framework 7.0, the `SpringExtension` uses a test-method scoped +`ExtensionContext` within `@Nested` test class hierarchies by default. However, the +`SpringExtension` can be configured to use a test-class scoped `ExtensionContext`. + +See the documentation for +xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-springextensionconfig[`@SpringExtensionConfig`] +for details. +==== + [TIP] ==== If you are developing a component that integrates with the Spring TestContext Framework diff --git a/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java b/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java index 6c3d17b6c98..84a68a76ab1 100644 --- a/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java +++ b/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java @@ -88,7 +88,12 @@ import org.springframework.util.ReflectionUtils.MethodFilter; * be configured to use a test-class scoped extension context by annotating a * top-level test class with * {@link SpringExtensionConfig#useTestClassScopedExtensionContext() - * @SpringExtensionConfig(useTestClassScopedExtensionContext = true)}. + * @SpringExtensionConfig(useTestClassScopedExtensionContext = true)}. Note, + * however, that the {@code SpringExtension} will always use a test-class scoped + * {@code ExtensionContext} if your top-level test class is configured to use JUnit + * Jupiter’s {@code @TestInstance(Lifecycle.PER_CLASS)} semantics, in which case + * there is no need to declare + * {@code @SpringExtensionConfig(useTestClassScopedExtensionContext = true)}. * *
NOTE: This class requires JUnit Jupiter 6.0 or higher. * diff --git a/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtensionConfig.java b/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtensionConfig.java index d7209e7654e..6d7d58a5750 100644 --- a/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtensionConfig.java +++ b/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtensionConfig.java @@ -60,6 +60,11 @@ public @interface SpringExtensionConfig { * {@code ExtensionContext}. Thus, there is no need to declare this annotation * attribute with a value of {@code false}. * + *
Similarly, if your top-level test class is configured to use JUnit Jupiter’s + * {@code @TestInstance(Lifecycle.PER_CLASS)} semantics, the {@code SpringExtension} + * will always use a test-class scoped {@code ExtensionContext}, and there is no need + * to declare {@code @SpringExtensionConfig(useTestClassScopedExtensionContext = true)}. + * * @see SpringExtension * @see SpringExtension#getTestInstantiationExtensionContextScope(org.junit.jupiter.api.extension.ExtensionContext) */