diff --git a/framework-docs/modules/ROOT/pages/appendix.adoc b/framework-docs/modules/ROOT/pages/appendix.adoc index dcfaacb27b3..b65a9ab4c45 100644 --- a/framework-docs/modules/ROOT/pages/appendix.adoc +++ b/framework-docs/modules/ROOT/pages/appendix.adoc @@ -138,6 +138,11 @@ xref:testing/testcontext-framework/ctx-management/context-pausing.adoc[Context P in the _Spring TestContext Framework_. See xref:testing/testcontext-framework/ctx-management/failure-threshold.adoc[Context Failure Threshold]. +| `spring.test.extension.context.scope` +| The default _extension context scope_ used by the `SpringExtension` in `@Nested` test +class hierarchies. See +xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-springextensionconfig[`@SpringExtensionConfig`]. + | `spring.test.enclosing.configuration` | The default _enclosing configuration inheritance mode_ to use if `@NestedTestConfiguration` is not present on a test class. See 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 03ea705eaab..1ae4d6c4e1b 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 @@ -3,7 +3,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): +and the JUnit Jupiter testing framework: * 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`] @@ -30,30 +30,40 @@ developer wishes to switch to test-class scoped semantics — the `SpringExtensi configured to use a test-class scoped `ExtensionContext` by annotating a top-level test class with `@SpringExtensionConfig(useTestClassScopedExtensionContext = true)`. +Alternatively, you can change the global default by setting the +`spring.test.extension.context.scope` property to `test_class`. The property is resolved +first via the +{spring-framework-api}/org/springframework/core/SpringProperties.html[`SpringProperties`] +mechanism (in a `spring.properties` file on the classpath or via JVM system properties, +for example `-Dspring.test.extension.context.scope=test_class`). If the Spring property +has not been set, the `SpringExtension` will attempt to resolve the property as a +https://docs.junit.org/current/running-tests/configuration-parameters.html[JUnit Platform configuration parameter] +as a fallback mechanism. If the property has not been set via either of those mechanisms, +the `SpringExtension` will use a test-method scoped extension context by default. Note, +however, that a `@SpringExtensionConfig` declaration always takes precedence over this +property. + [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)`. +If a test class uses JUnit Jupiter's `@TestInstance(Lifecycle.PER_CLASS)` semantics, the +`SpringExtension` will always use a test-class scoped `ExtensionContext`, and +configuration via `@SpringExtensionConfig(useTestClassScopedExtensionContext = true)` or +the `spring.test.extension.context.scope` property will have no effect for that test +class. ==== [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] -==== +In addition, 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. +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]] 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 54b9cd7b5cf..5f77d9e08b4 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 @@ -396,11 +396,10 @@ recursively. ==== 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. +`SpringExtension` can be configured to use a test-class scoped `ExtensionContext` — for +example via `@SpringExtensionConfig` or the `spring.test.extension.context.scope` Spring +property (see +xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-springextensionconfig[`@SpringExtensionConfig`]). ==== [TIP] diff --git a/spring-test/src/main/java/org/springframework/test/context/TestConstructor.java b/spring-test/src/main/java/org/springframework/test/context/TestConstructor.java index 1e96aeb6e3d..9e667d4d24c 100644 --- a/spring-test/src/main/java/org/springframework/test/context/TestConstructor.java +++ b/spring-test/src/main/java/org/springframework/test/context/TestConstructor.java @@ -87,8 +87,7 @@ public @interface TestConstructor { * semantics by default. *
May alternatively be configured via the * {@link org.springframework.core.SpringProperties SpringProperties} - * mechanism. - *
This property may also be configured as a + * mechanism or as a * JUnit * Platform configuration parameter. * @see #autowireMode 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 84a68a76ab1..8a9f35d410d 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 @@ -23,6 +23,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.Arrays; import java.util.List; +import java.util.Locale; import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.AfterAll; @@ -42,12 +43,14 @@ import org.junit.jupiter.api.extension.ExtensionContext.Store; import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.extension.ParameterResolver; import org.junit.jupiter.api.extension.TestInstancePostProcessor; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension; import org.junit.jupiter.api.parallel.ExecutionMode; import org.junit.platform.commons.annotation.Testable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.ParameterResolutionDelegate; import org.springframework.context.ApplicationContext; +import org.springframework.core.SpringProperties; import org.springframework.core.annotation.MergedAnnotation; import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; @@ -61,9 +64,9 @@ import org.springframework.test.context.support.PropertyProvider; import org.springframework.test.context.support.TestConstructorUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; -import org.springframework.util.ConcurrentLruCache; import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils.MethodFilter; +import org.springframework.util.StringUtils; /** * {@code SpringExtension} integrates the Spring TestContext Framework @@ -85,15 +88,15 @@ import org.springframework.util.ReflectionUtils.MethodFilter; * 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 {@code SpringExtension} can - * be configured to use a test-class scoped extension context by annotating a - * top-level test class with + * be configured by annotating a top-level test class with * {@link SpringExtensionConfig#useTestClassScopedExtensionContext() - * @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)}. + * @SpringExtensionConfig(useTestClassScopedExtensionContext = true)} or by + * setting the {@value #EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME} property to + * {@link ExtensionContextScope#TEST_CLASS test_class}. Note that an explicit + * {@code @SpringExtensionConfig} declaration overrides the globally configured + * property. Furthermore, the {@code SpringExtension} will always use a test-class + * scoped {@code ExtensionContext} for a test class that is configured to use JUnit + * Jupiter’s {@code @TestInstance(Lifecycle.PER_CLASS)} semantics. * *
NOTE: This class requires JUnit Jupiter 6.0 or higher. * @@ -112,12 +115,42 @@ public class SpringExtension implements BeforeAllCallback, AfterAllCallback, Tes BeforeEachCallback, AfterEachCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback, ParameterResolver { + /** + * JVM system property used to configure the default {@link ExtensionContextScope} + * for the {@code SpringExtension}: {@value}. + *
Acceptable values include enum constants defined in {@code ExtensionContextScope}, + * ignoring case. For example, the default may be changed to + * {@link ExtensionContextScope#TEST_CLASS} by supplying the following JVM + * system property via the command line. + *
-Dspring.test.extension.context.scope=test_class+ *
If the property is not set, {@link ExtensionContextScope#TEST_METHOD} + * semantics will apply. Note, however, that {@code @SpringExtensionConfig} + * takes precedence over this property. + *
May alternatively be configured via the
+ * {@link org.springframework.core.SpringProperties SpringProperties}
+ * mechanism or as a
+ * JUnit
+ * Platform configuration parameter.
+ * @since 7.0.7
+ * @see ExtensionContextScope
+ * @see SpringExtensionConfig
+ */
+ public static final String EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME = "spring.test.extension.context.scope";
+
/**
* {@link Namespace} in which {@code TestContextManagers} are stored, keyed
* by test class.
*/
private static final Namespace TEST_CONTEXT_MANAGER_NAMESPACE = Namespace.create(SpringExtension.class);
+ /**
+ * {@link Namespace} in which the resolved default {@link ExtensionContextScope}
+ * is stored.
+ * @since 7.0.7
+ */
+ private static final Namespace DEFAULT_EXTENSION_CONTEXT_SCOPE_NAMESPACE =
+ Namespace.create(SpringExtension.class.getName() + "#default.extension.context.scope");
+
/**
* {@link Namespace} in which {@code @Autowired} validation error messages
* are stored, keyed by test class.
@@ -139,14 +172,6 @@ public class SpringExtension implements BeforeAllCallback, AfterAllCallback, Tes
private static final Namespace RECORD_APPLICATION_EVENTS_VALIDATION_NAMESPACE =
Namespace.create(SpringExtension.class.getName() + "#recordApplicationEvents.validation");
- /**
- * LRU cache for {@link SpringExtensionConfig#useTestClassScopedExtensionContext()}
- * mappings, keyed by test class.
- * @since 7.0
- */
- private static final ConcurrentLruCache This can be effectively overridden by annotating a test class with
- * {@code @SpringExtensionConfig(useTestClassScopedExtensionContext = true)}.
- * See the {@linkplain SpringExtension class-level Javadoc} for further details.
+ * Returns {@link TestInstantiationAwareExtension.ExtensionContextScope#TEST_METHOD
+ * ExtensionContextScope.TEST_METHOD}.
+ * This can be overridden locally via the
+ * {@link SpringExtensionConfig @SpringExtensionConfig} annotation or
+ * globally via the {@value #EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME}
+ * property. See the {@linkplain SpringExtension class-level Javadoc} for further
+ * details.
* @since 7.0
* @see SpringExtensionConfig#useTestClassScopedExtensionContext()
+ * @see #EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME
+ * @see ExtensionContextScope
*/
@Override
- public ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {
- return ExtensionContextScope.TEST_METHOD;
+ public TestInstantiationAwareExtension.ExtensionContextScope getTestInstantiationExtensionContextScope(
+ ExtensionContext rootContext) {
+
+ return TestInstantiationAwareExtension.ExtensionContextScope.TEST_METHOD;
}
/**
@@ -464,20 +496,19 @@ public class SpringExtension implements BeforeAllCallback, AfterAllCallback, Tes
}
/**
- * Find the properly {@linkplain ExtensionContextScope scoped} {@link ExtensionContext}
- * for the supplied test class.
+ * Find the properly scoped {@link ExtensionContext} for the supplied test class.
* If the supplied {@code ExtensionContext} is already properly scoped, it
- * will be returned. Otherwise, if the test class is annotated with
- * {@code @SpringExtensionConfig(useTestClassScopedExtensionContext = true)},
- * this method searches the {@code ExtensionContext} hierarchy for an
- * {@code ExtensionContext} whose test class is the same as the supplied
- * test class.
+ * will be returned. Otherwise, if test-class scoped semantics apply (see
+ * {@linkplain SpringExtension class-level Javadoc}), this method searches the
+ * {@code ExtensionContext} hierarchy for an {@code ExtensionContext} whose test
+ * class is the same as the supplied test class.
* @since 7.0
* @see SpringExtensionConfig#useTestClassScopedExtensionContext()
+ * @see #EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME
* @see ExtensionContextScope
*/
private static ExtensionContext findProperlyScopedExtensionContext(Class> testClass, ExtensionContext context) {
- if (useTestClassScopedExtensionContextCache.get(testClass)) {
+ if (shouldUseTestClassScopedExtensionContext(testClass, context)) {
while (context.getRequiredTestClass() != testClass) {
context = context.getParent().get();
}
@@ -486,13 +517,11 @@ public class SpringExtension implements BeforeAllCallback, AfterAllCallback, Tes
}
/**
- * Determine if the supplied test class, or one of its enclosing classes, is annotated
- * with {@code @SpringExtensionConfig(useTestClassScopedExtensionContext = true)}.
+ * Determine whether test-class scoped {@code ExtensionContext} semantics apply
+ * for the supplied test class.
* @since 7.0
- * @see SpringExtensionConfig#useTestClassScopedExtensionContext()
- * @see #useTestClassScopedExtensionContextCache
*/
- private static boolean useTestClassScopedExtensionContext(Class> testClass) {
+ private static boolean shouldUseTestClassScopedExtensionContext(Class> testClass, ExtensionContext context) {
MergedAnnotation Furthermore, this attribute takes precedence over global configuration
+ * of the {@code spring.test.extension.context.scope} property.
+ *
* @see SpringExtension
- * @see SpringExtension#getTestInstantiationExtensionContextScope(org.junit.jupiter.api.extension.ExtensionContext)
+ * @see SpringExtension#EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME
+ * @see SpringExtension.ExtensionContextScope
*/
boolean useTestClassScopedExtensionContext();
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/SpringExtensionExtensionContextScopeTests.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/SpringExtensionExtensionContextScopeTests.java
new file mode 100644
index 00000000000..92de037a721
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/SpringExtensionExtensionContextScopeTests.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2002-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.test.context.junit.jupiter;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.platform.testkit.engine.EngineTestKit;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.SpringProperties;
+import org.springframework.core.env.Environment;
+import org.springframework.test.context.NestedTestConfiguration;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit.jupiter.SpringExtension.ExtensionContextScope;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
+import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.OVERRIDE;
+import static org.springframework.test.context.junit.jupiter.SpringExtension.EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME;
+import static org.springframework.test.context.junit.jupiter.SpringExtension.ExtensionContextScope.TEST_CLASS;
+import static org.springframework.test.context.junit.jupiter.SpringExtension.ExtensionContextScope.TEST_METHOD;
+
+/**
+ * Tests for {@link SpringExtension.ExtensionContextScope} and
+ * {@link SpringExtension#EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME}.
+ *
+ * @author Sam Brannen
+ * @since 7.0.7
+ */
+class SpringExtensionExtensionContextScopeTests {
+
+ @Test
+ void extensionContextScopeFromString() {
+ assertThat(ExtensionContextScope.from(null)).isNull();
+ assertThat(ExtensionContextScope.from("")).isNull();
+ assertThat(ExtensionContextScope.from(" ")).isNull();
+ assertThat(ExtensionContextScope.from("bogus")).isNull();
+
+ assertThat(ExtensionContextScope.from("TEST_METHOD")).isSameAs(TEST_METHOD);
+ assertThat(ExtensionContextScope.from("test_method")).isSameAs(TEST_METHOD);
+ assertThat(ExtensionContextScope.from("Test_Method")).isSameAs(TEST_METHOD);
+
+ assertThat(ExtensionContextScope.from("TEST_CLASS")).isSameAs(TEST_CLASS);
+ assertThat(ExtensionContextScope.from("test_class")).isSameAs(TEST_CLASS);
+ assertThat(ExtensionContextScope.from("Test_Class")).isSameAs(TEST_CLASS);
+ }
+
+ @Test
+ void invalidExtensionContextScopeIsRejectedWhenConfiguredViaSpringProperties() {
+ SpringProperties.setProperty(EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME, "bogus");
+ try {
+ EngineTestKit.engine("junit-jupiter")
+ .selectors(selectClass(InvalidScopeTestCase.class))
+ .execute()
+ .testEvents()
+ .assertStatistics(stats -> stats.started(1).failed(1));
+ }
+ finally {
+ SpringProperties.setProperty(EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME, null);
+ }
+ }
+
+ @Test
+ void invalidExtensionContextScopeIsRejectedWhenConfiguredViaJUnitConfigurationParameter() {
+ EngineTestKit.engine("junit-jupiter")
+ .selectors(selectClass(InvalidScopeTestCase.class))
+ .configurationParameter(EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME, "bogus")
+ .execute()
+ .testEvents()
+ .assertStatistics(stats -> stats.started(1).failed(1));
+ }
+
+ @Test
+ void testClassScopeConfiguredViaSpringProperties() {
+ SpringProperties.setProperty(EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME, TEST_CLASS.name());
+ try {
+ var results = EngineTestKit.engine("junit-jupiter")
+ .selectors(selectClass(GlobalClassScopedConfigurationTestCase.class))
+ .execute();
+ results.containerEvents()
+ .assertStatistics(stats -> stats.started(3).succeeded(3).failed(0));
+ results.testEvents()
+ .assertStatistics(stats -> stats.started(2).succeeded(2).failed(0));
+ }
+ finally {
+ SpringProperties.setProperty(EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME, null);
+ }
+ }
+
+ @Test
+ void testClassScopeConfiguredViaJUnitConfigurationParameter() {
+ var results = EngineTestKit.engine("junit-jupiter")
+ .selectors(selectClass(GlobalClassScopedConfigurationTestCase.class))
+ .configurationParameter(EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME, TEST_CLASS.name())
+ .execute();
+ results.containerEvents()
+ .assertStatistics(stats -> stats.started(3).succeeded(3).failed(0));
+ results.testEvents()
+ .assertStatistics(stats -> stats.started(2).succeeded(2).failed(0));
+ }
+
+ @Test
+ void springExtensionConfigOverridesGlobalTestClassScopeConfiguration() {
+ EngineTestKit.engine("junit-jupiter")
+ .selectors(selectClass(SpringExtensionConfigOverridesGlobalPropertyTestCase.class))
+ .configurationParameter(EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME, TEST_CLASS.name())
+ .execute()
+ .testEvents()
+ .assertStatistics(stats -> stats.started(2).succeeded(2).failed(0));
+ }
+
+
+ @SpringJUnitConfig
+ static class InvalidScopeTestCase {
+
+ @Test
+ void test() {
+ // no-op
+ }
+ }
+
+ @SpringJUnitConfig
+ @TestPropertySource(properties = "p1 = v1")
+ @NestedTestConfiguration(OVERRIDE)
+ @FailingTestCase
+ static class GlobalClassScopedConfigurationTestCase {
+
+ @Autowired
+ Environment env1;
+
+ @Test
+ void propertiesInEnvironment() {
+ assertThat(env1.getProperty("p1")).isEqualTo("v1");
+ }
+
+ @Nested
+ @SpringJUnitConfig(Config.class)
+ @TestPropertySource(properties = "p2 = v2")
+ class ConfigOverriddenByDefaultTests {
+
+ @Autowired
+ Environment env2;
+
+ @Test
+ void propertiesInEnvironment() {
+ assertThat(env1.getProperty("p1")).isEqualTo("v1");
+ assertThat(env1).isNotSameAs(env2);
+ assertThat(env2.getProperty("p1")).isNull();
+ assertThat(env2.getProperty("p2")).isEqualTo("v2");
+ }
+ }
+
+ @Configuration
+ static class Config {
+ }
+ }
+
+ @SpringJUnitConfig
+ @SpringExtensionConfig(useTestClassScopedExtensionContext = false)
+ @TestPropertySource(properties = "p1 = v1")
+ @NestedTestConfiguration(OVERRIDE)
+ static class SpringExtensionConfigOverridesGlobalPropertyTestCase {
+
+ @Autowired
+ Environment env1;
+
+ @Test
+ void propertiesInEnvironment() {
+ assertThat(env1.getProperty("p1")).isEqualTo("v1");
+ }
+
+ @Nested
+ @SpringJUnitConfig(Config.class)
+ @TestPropertySource(properties = "p2 = v2")
+ class ConfigOverriddenByDefaultTests {
+
+ @Autowired
+ Environment env2;
+
+ @Test
+ void propertiesInEnvironment() {
+ assertThat(env1).isSameAs(env2);
+ assertThat(env2.getProperty("p1")).isNull();
+ assertThat(env2.getProperty("p2")).isEqualTo("v2");
+ }
+ }
+
+ @Configuration
+ static class Config {
+ }
+ }
+
+}