Browse Source

Merge branch '7.0.x'

pull/36514/head
Sam Brannen 1 week ago
parent
commit
7c834224a2
  1. 5
      framework-docs/modules/ROOT/pages/appendix.adoc
  2. 36
      framework-docs/modules/ROOT/pages/testing/annotations/integration-junit-jupiter.adoc
  3. 9
      framework-docs/modules/ROOT/pages/testing/testcontext-framework/support-classes.adoc
  4. 3
      spring-test/src/main/java/org/springframework/test/context/TestConstructor.java
  5. 192
      spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java
  6. 6
      spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtensionConfig.java
  7. 208
      spring-test/src/test/java/org/springframework/test/context/junit/jupiter/SpringExtensionExtensionContextScopeTests.java

5
framework-docs/modules/ROOT/pages/appendix.adoc

@ -138,6 +138,11 @@ xref:testing/testcontext-framework/ctx-management/context-pausing.adoc[Context P @@ -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

36
framework-docs/modules/ROOT/pages/testing/annotations/integration-junit-jupiter.adoc

@ -3,7 +3,7 @@ @@ -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 @@ -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]]

9
framework-docs/modules/ROOT/pages/testing/testcontext-framework/support-classes.adoc

@ -396,11 +396,10 @@ recursively. @@ -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]

3
spring-test/src/main/java/org/springframework/test/context/TestConstructor.java

@ -87,8 +87,7 @@ public @interface TestConstructor { @@ -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

192
spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java

@ -23,6 +23,7 @@ import java.lang.reflect.Method; @@ -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; @@ -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; @@ -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; @@ -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 &mdash; or if a developer wishes to
* switch to test-class scoped semantics &mdash; 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()
* &#64;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
* Jupiters {@code @TestInstance(Lifecycle.PER_CLASS)} semantics, in which case
* there is no need to declare
* {@code @SpringExtensionConfig(useTestClassScopedExtensionContext = true)}.
* &#64;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
* Jupiters {@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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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;
}
}
}
}

6
spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtensionConfig.java

@ -65,8 +65,12 @@ public @interface SpringExtensionConfig { @@ -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
spring-test/src/test/java/org/springframework/test/context/junit/jupiter/SpringExtensionExtensionContextScopeTests.java

@ -0,0 +1,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 {
}
}
}
Loading…
Cancel
Save