mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Merge branch '7.0.x'
This commit is contained in:
@@ -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
|
||||
|
||||
+23
-13
@@ -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]]
|
||||
|
||||
+4
-5
@@ -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]
|
||||
|
||||
@@ -87,8 +87,7 @@ public @interface TestConstructor {
|
||||
* semantics by default.
|
||||
* <p>May alternatively be configured via the
|
||||
* {@link org.springframework.core.SpringProperties SpringProperties}
|
||||
* mechanism.
|
||||
* <p>This property may also be configured as a
|
||||
* mechanism or as a
|
||||
* <a href="https://docs.junit.org/current/running-tests/configuration-parameters.html">JUnit
|
||||
* Platform configuration parameter</a>.
|
||||
* @see #autowireMode
|
||||
|
||||
+155
-37
@@ -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 <em>Spring TestContext Framework</em>
|
||||
@@ -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.
|
||||
*
|
||||
* <p><strong>NOTE:</strong> 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}.
|
||||
* <p>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.
|
||||
* <pre style="code">-Dspring.test.extension.context.scope=test_class</pre>
|
||||
* <p>If the property is not set, {@link ExtensionContextScope#TEST_METHOD}
|
||||
* semantics will apply. Note, however, that {@code @SpringExtensionConfig}
|
||||
* takes precedence over this property.
|
||||
* <p>May alternatively be configured via the
|
||||
* {@link org.springframework.core.SpringProperties SpringProperties}
|
||||
* mechanism or as a
|
||||
* <a href="https://docs.junit.org/current/running-tests/configuration-parameters.html">JUnit
|
||||
* Platform configuration parameter</a>.
|
||||
* @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<Class<?>, Boolean> useTestClassScopedExtensionContextCache =
|
||||
new ConcurrentLruCache<>(32, SpringExtension::useTestClassScopedExtensionContext);
|
||||
|
||||
// Note that @Test, @TestFactory, @TestTemplate, @RepeatedTest, and @ParameterizedTest
|
||||
// are all meta-annotated with @Testable.
|
||||
private static final List<Class<? extends Annotation>> JUPITER_ANNOTATION_TYPES =
|
||||
@@ -157,16 +182,23 @@ public class SpringExtension implements BeforeAllCallback, AfterAllCallback, Tes
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@link ExtensionContextScope#TEST_METHOD ExtensionContextScope.TEST_METHOD}.
|
||||
* <p>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}.
|
||||
* <p>This can be overridden <em>locally</em> via the
|
||||
* {@link SpringExtensionConfig @SpringExtensionConfig} annotation or
|
||||
* <em>globally</em> 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.
|
||||
* <p>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<SpringExtensionConfig> mergedAnnotation =
|
||||
MergedAnnotations.search(SearchStrategy.TYPE_HIERARCHY)
|
||||
.withEnclosingClasses(ClassUtils::isInnerClass)
|
||||
@@ -509,7 +538,96 @@ public class SpringExtension implements BeforeAllCallback, AfterAllCallback, Tes
|
||||
return mergedAnnotation.getBoolean("useTestClassScopedExtensionContext");
|
||||
}
|
||||
|
||||
return false;
|
||||
return (resolveDefaultExtensionContextScope(context) == ExtensionContextScope.TEST_CLASS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the default {@link ExtensionContextScope} from the
|
||||
* {@value #EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME} property, first via
|
||||
* {@link SpringProperties} and then via
|
||||
* {@link ExtensionContext#getConfigurationParameter(String)} as a fallback
|
||||
* strategy if the Spring property is not set.
|
||||
* @param context the current {@code ExtensionContext}
|
||||
* @return the resolved scope, or {@link ExtensionContextScope#TEST_METHOD}
|
||||
* if the property is not set
|
||||
* @since 7.0.7
|
||||
*/
|
||||
private static ExtensionContextScope resolveDefaultExtensionContextScope(ExtensionContext context) {
|
||||
return context.getRoot().getStore(DEFAULT_EXTENSION_CONTEXT_SCOPE_NAMESPACE)
|
||||
.computeIfAbsent(ExtensionContextScope.class, key -> {
|
||||
String springValue = SpringProperties.getProperty(EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME);
|
||||
ExtensionContextScope scope = ExtensionContextScope.from(springValue);
|
||||
if (scope != null) {
|
||||
return scope;
|
||||
}
|
||||
rejectUnsupportedScope(springValue);
|
||||
|
||||
String junitValue = context.getConfigurationParameter(EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME)
|
||||
.orElse(null);
|
||||
scope = ExtensionContextScope.from(junitValue);
|
||||
if (scope != null) {
|
||||
return scope;
|
||||
}
|
||||
rejectUnsupportedScope(junitValue);
|
||||
|
||||
// Default to test-method scope.
|
||||
return ExtensionContextScope.TEST_METHOD;
|
||||
}, ExtensionContextScope.class);
|
||||
}
|
||||
|
||||
private static void rejectUnsupportedScope(@Nullable String scope) {
|
||||
if (StringUtils.hasText(scope)) {
|
||||
throw new IllegalArgumentException("Unsupported value '%s' for property '%s'"
|
||||
.formatted(scope.strip(), EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enumeration of <em>extension context scopes</em> for configuring how the
|
||||
* {@link SpringExtension} resolves an {@link ExtensionContext} within
|
||||
* {@code @Nested} test class hierarchies.
|
||||
*
|
||||
* @since 7.0.7
|
||||
* @see SpringExtension#EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME
|
||||
* @see SpringExtensionConfig
|
||||
* @see org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope
|
||||
*/
|
||||
public enum ExtensionContextScope {
|
||||
|
||||
/**
|
||||
* Use a test-method scoped {@link ExtensionContext} within {@code @Nested}
|
||||
* test class hierarchies.
|
||||
* @see org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope#TEST_METHOD
|
||||
*/
|
||||
TEST_METHOD,
|
||||
|
||||
/**
|
||||
* Use a test-class scoped {@link ExtensionContext} within {@code @Nested}
|
||||
* test class hierarchies.
|
||||
* @see org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope#DEFAULT
|
||||
*/
|
||||
TEST_CLASS;
|
||||
|
||||
|
||||
/**
|
||||
* Get the {@link ExtensionContextScope} enum constant with the supplied name,
|
||||
* {@linkplain String#strip() stripped} and ignoring case.
|
||||
* @param name the name of the enum constant to retrieve
|
||||
* @return the corresponding enum constant, or {@code null} if not found
|
||||
* @see ExtensionContextScope#valueOf(String)
|
||||
*/
|
||||
static @Nullable ExtensionContextScope from(@Nullable String name) {
|
||||
if (!StringUtils.hasText(name)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return ExtensionContextScope.valueOf(name.strip().toUpperCase(Locale.ROOT));
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-1
@@ -65,8 +65,12 @@ public @interface SpringExtensionConfig {
|
||||
* will always use a test-class scoped {@code ExtensionContext}, and there is no need
|
||||
* to declare {@code @SpringExtensionConfig(useTestClassScopedExtensionContext = true)}.
|
||||
*
|
||||
* <p>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();
|
||||
|
||||
|
||||
+208
@@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user