Browse Source
Prior to this commit, it was only possible to register a ContextCustomizerFactory in the TestContext framework (TCF) via the SpringFactoriesLoader mechanism. This commit introduces support for declarative registration of a ContextCustomizerFactory local to a test class via a new @ContextCustomizerFactories annotation. Closes gh-26148pull/31226/head
Sam Brannen
1 year ago
27 changed files with 1085 additions and 70 deletions
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
[[spring-testing-annotation-contextcustomizerfactories]] |
||||
= `@ContextCustomizerFactories` |
||||
|
||||
`@ContextCustomizerFactories` is used to register `ContextCustomizerFactory` |
||||
implementations for a particular test class, its subclasses, and its nested classes. If |
||||
you wish to register a factory globally, you should register it via the automatic |
||||
discovery mechanism described in |
||||
xref:testing/testcontext-framework/ctx-management/context-customizers.adoc[`ContextCustomizerFactory` Configuration]. |
||||
|
||||
The following example shows how to register two `ContextCustomizerFactory` implementations: |
||||
|
||||
[tabs] |
||||
====== |
||||
Java:: |
||||
+ |
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
||||
---- |
||||
@ContextConfiguration |
||||
@ContextCustomizerFactories({CustomContextCustomizerFactory.class, AnotherContextCustomizerFactory.class}) // <1> |
||||
class CustomContextCustomizerFactoryTests { |
||||
// class body... |
||||
} |
||||
---- |
||||
<1> Register two `ContextCustomizerFactory` implementations. |
||||
|
||||
Kotlin:: |
||||
+ |
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
||||
---- |
||||
@ContextConfiguration |
||||
@ContextCustomizerFactories([CustomContextCustomizerFactory::class, AnotherContextCustomizerFactory::class]) // <1> |
||||
class CustomContextCustomizerFactoryTests { |
||||
// class body... |
||||
} |
||||
---- |
||||
<1> Register two `ContextCustomizerFactory` implementations. |
||||
====== |
||||
|
||||
|
||||
By default, `@ContextCustomizerFactories` provides support for inheriting factories from |
||||
superclasses or enclosing classes. See |
||||
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-nested-test-configuration[`@Nested` test class configuration] and the |
||||
{api-spring-framework}/test/context/ContextCustomizerFactories.html[`@ContextCustomizerFactories` |
||||
javadoc] for an example and further details. |
||||
|
@ -0,0 +1,69 @@
@@ -0,0 +1,69 @@
|
||||
[[testcontext-context-customizers]] |
||||
= Configuration Configuration with Context Customizers |
||||
|
||||
A `ContextCustomizer` is responsible for customizing the supplied |
||||
`ConfigurableApplicationContext` after bean definitions have been loaded into the context |
||||
but before the context has been refreshed. |
||||
|
||||
A `ContextCustomizerFactory` is responsible for creating a `ContextCustomizer`, based on |
||||
some custom logic which determines if the `ContextCustomizer` is necessary for a given |
||||
test class -- for example, based on the presence of a certain annotation. Factories are |
||||
invoked after `ContextLoaders` have processed context configuration attributes for a test |
||||
class but before the `MergedContextConfiguration` is created. |
||||
|
||||
For example, Spring Framework provides the following `ContextCustomizerFactory` |
||||
implementation which is registered by default: |
||||
|
||||
`MockServerContainerContextCustomizerFactory`:: Creates a |
||||
`MockServerContainerContextCustomizer` if WebSocket support is present in the classpath |
||||
and the test class or one of its enclosing classes is annotated or meta-annotated with |
||||
`@WebAppConfiguration`. `MockServerContainerContextCustomizer` instantiates a new |
||||
`MockServerContainer` and stores it in the `ServletContext` under the attribute named |
||||
`jakarta.websocket.server.ServerContainer`. |
||||
|
||||
|
||||
[[testcontext-context-customizers-registration]] |
||||
== Registering `ContextCustomizerFactory` Implementations |
||||
|
||||
You can register `ContextCustomizerFactory` implementations explicitly for a test class, its |
||||
subclasses, and its nested classes by using the `@ContextCustomizerFactories` annotation. See |
||||
xref:testing/annotations/integration-spring/annotation-contextcustomizerfactories.adoc[annotation support] |
||||
and the javadoc for |
||||
{api-spring-framework}/test/context/ContextCustomizerFactories.html[`@ContextCustomizerFactories`] |
||||
for details and examples. |
||||
|
||||
|
||||
[[testcontext-context-customizers-automatic-discovery]] |
||||
== Automatic Discovery of Default `ContextCustomizerFactory` Implementations |
||||
|
||||
Registering `ContextCustomizerFactory` implementations by using `@ContextCustomizerFactories` is |
||||
suitable for custom factories that are used in limited testing scenarios. However, it can |
||||
become cumbersome if a custom factory needs to be used across an entire test suite. This |
||||
issue is addressed through support for automatic discovery of default |
||||
`ContextCustomizerFactory` implementations through the `SpringFactoriesLoader` mechanism. |
||||
|
||||
Specifically, the modules that make up the testing support in Spring Framework and Spring |
||||
Boot declare all core default `ContextCustomizerFactory` implementations under the |
||||
`org.springframework.test.context.ContextCustomizerFactory` key in their |
||||
`META-INF/spring.factories` properties files. Third-party frameworks and developers can |
||||
contribute their own `ContextCustomizerFactory` implementations to the list of default |
||||
factories in the same manner through their own `META-INF/spring.factories` properties |
||||
files. |
||||
|
||||
|
||||
[[testcontext-context-customizers-merging]] |
||||
== Merging `ContextCustomizerFactory` Implementations |
||||
|
||||
If a custom `ContextCustomizerFactory` is registered via `@ContextCustomizerFactories`, it |
||||
will be _merged_ with the default factories that have been registered using the aforementioned |
||||
xref:testing/testcontext-framework/ctx-management/context-customizers.adoc#testcontext-context-customizers-automatic-discovery[automatic discovery mechanism]. |
||||
|
||||
The merging algorithm ensures that duplicates are removed from the list and that locally |
||||
declared factories are appended to the list of default factories when merged. |
||||
|
||||
[TIP] |
||||
==== |
||||
To replace the default factories for a test class, its subclasses, and its nested |
||||
classes, you can set the `mergeMode` attribute of `@ContextCustomizerFactories` to |
||||
`MergeMode.REPLACE_DEFAULTS`. |
||||
==== |
@ -0,0 +1,125 @@
@@ -0,0 +1,125 @@
|
||||
/* |
||||
* Copyright 2002-2023 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; |
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Inherited; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
import org.springframework.core.annotation.AliasFor; |
||||
|
||||
/** |
||||
* {@code @ContextCustomizerFactories} defines class-level metadata for configuring |
||||
* which {@link ContextCustomizerFactory} implementations should be registered with |
||||
* the <em>Spring TestContext Framework</em>. |
||||
* |
||||
* <p>{@code @ContextCustomizerFactories} is used to register factories for a |
||||
* particular test class, its subclasses, and its nested classes. If you wish to |
||||
* register a factory globally, you should register it via the automatic discovery |
||||
* mechanism described in {@link ContextCustomizerFactory}. |
||||
* |
||||
* <p>This annotation may be used as a <em>meta-annotation</em> to create custom |
||||
* <em>composed annotations</em>. In addition, this annotation will be inherited |
||||
* from an enclosing test class by default. See |
||||
* {@link NestedTestConfiguration @NestedTestConfiguration} for details. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 6.1 |
||||
* @see ContextCustomizerFactory |
||||
* @see ContextCustomizer |
||||
*/ |
||||
@Target(ElementType.TYPE) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
@Inherited |
||||
public @interface ContextCustomizerFactories { |
||||
|
||||
/** |
||||
* Alias for {@link #factories}. |
||||
* <p>This attribute may <strong>not</strong> be used in conjunction with |
||||
* {@link #factories}, but it may be used instead of {@link #factories}. |
||||
*/ |
||||
@AliasFor("factories") |
||||
Class<? extends ContextCustomizerFactory>[] value() default {}; |
||||
|
||||
/** |
||||
* The {@link ContextCustomizerFactory} implementations to register. |
||||
* <p>This attribute may <strong>not</strong> be used in conjunction with |
||||
* {@link #value}, but it may be used instead of {@link #value}. |
||||
*/ |
||||
@AliasFor("value") |
||||
Class<? extends ContextCustomizerFactory>[] factories() default {}; |
||||
|
||||
/** |
||||
* Whether the configured set of {@link #factories} from superclasses and |
||||
* enclosing classes should be <em>inherited</em>. |
||||
* <p>The default value is {@code true}, which means that an annotated class |
||||
* will <em>inherit</em> the factories defined by an annotated superclass or |
||||
* enclosing class. Specifically, the factories for an annotated class will be |
||||
* appended to the list of factories defined by an annotated superclass or |
||||
* enclosing class. Thus, subclasses and nested classes have the option of |
||||
* <em>extending</em> the list of factories. |
||||
* <p>If {@code inheritListeners} is set to {@code false}, the factories for |
||||
* the annotated class will <em>shadow</em> and effectively replace any |
||||
* factories defined by a superclass or enclosing class. |
||||
*/ |
||||
boolean inheritFactories() default true; |
||||
|
||||
/** |
||||
* The <em>merge mode</em> to use when {@code @ContextCustomizerFactories} is |
||||
* declared on a class that does <strong>not</strong> inherit factories from |
||||
* a superclass or enclosing class. |
||||
* <p>Can be set to {@link MergeMode#REPLACE_DEFAULTS REPLACE_DEFAULTS} to |
||||
* have locally declared factories replace the default factories. |
||||
* <p>The mode is ignored if factories are inherited from a superclass or |
||||
* enclosing class. |
||||
* <p>Defaults to {@link MergeMode#MERGE_WITH_DEFAULTS MERGE_WITH_DEFAULTS}. |
||||
* @see MergeMode |
||||
*/ |
||||
MergeMode mergeMode() default MergeMode.MERGE_WITH_DEFAULTS; |
||||
|
||||
|
||||
/** |
||||
* Enumeration of <em>modes</em> that dictate whether explicitly declared |
||||
* factories are merged with the default factories when |
||||
* {@code @ContextCustomizerFactories} is declared on a class that does |
||||
* <strong>not</strong> inherit factories from a superclass or enclosing |
||||
* class. |
||||
*/ |
||||
enum MergeMode { |
||||
|
||||
/** |
||||
* Indicates that locally declared factories should be merged with the |
||||
* default factories. |
||||
* <p>The merging algorithm ensures that duplicates are removed from the |
||||
* list and that locally declared factories are appended to the list of |
||||
* default factories when merged. |
||||
*/ |
||||
MERGE_WITH_DEFAULTS, |
||||
|
||||
/** |
||||
* Indicates that locally declared factories should replace the default |
||||
* factories. |
||||
*/ |
||||
REPLACE_DEFAULTS |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
|
||||
/* |
||||
* Copyright 2002-2023 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.customizers; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.springframework.test.context.ContextConfigurationAttributes; |
||||
import org.springframework.test.context.ContextCustomizer; |
||||
import org.springframework.test.context.ContextCustomizerFactory; |
||||
|
||||
/** |
||||
* @author Sam Brannen |
||||
* @since 6.1 |
||||
*/ |
||||
class BarContextCustomizerFactory implements ContextCustomizerFactory { |
||||
|
||||
@Override |
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass, |
||||
List<ContextConfigurationAttributes> configAttributes) { |
||||
return (context, mergedConfig) -> context.getBeanFactory().registerSingleton("bar", "baz"); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
|
||||
/* |
||||
* Copyright 2002-2023 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.customizers; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.beans.factory.annotation.Qualifier; |
||||
import org.springframework.test.context.ContextCustomizer; |
||||
import org.springframework.test.context.ContextCustomizerFactories; |
||||
import org.springframework.test.context.ContextCustomizerFactory; |
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Integration test which verifies support for {@link ContextCustomizerFactory} |
||||
* and {@link ContextCustomizer} when a custom factory is registered declaratively |
||||
* via {@link ContextCustomizerFactories @ContextCustomizerFactories}. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 6.1 |
||||
*/ |
||||
@SpringJUnitConfig({}) |
||||
@CustomizeWithFruit |
||||
@CustomizeWithFoo |
||||
@CustomizeWithBar |
||||
class ContextCustomizerDeclarativeRegistrationComposedAnnotationTests { |
||||
|
||||
// GlobalFruitContextCustomizerFactory is registered via spring.factories
|
||||
@Autowired(required = false) |
||||
@Qualifier("global$fruit") |
||||
String fruit; |
||||
|
||||
@Autowired(required = false) |
||||
@Qualifier("foo") |
||||
String foo; |
||||
|
||||
@Autowired(required = false) |
||||
@Qualifier("bar") |
||||
String bar; |
||||
|
||||
|
||||
@Test |
||||
void injectedBean() { |
||||
// registered globally via spring.factories
|
||||
assertThat(fruit).isEqualTo("apple, banana, cherry"); |
||||
|
||||
// From local @ContextCustomizerFactories
|
||||
assertThat(foo).isEqualTo("bar"); |
||||
|
||||
// @ContextCustomizerFactories is not currently supported as a repeatable annotation.
|
||||
assertThat(bar).isNull(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,77 @@
@@ -0,0 +1,77 @@
|
||||
/* |
||||
* Copyright 2002-2023 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.customizers; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.beans.factory.annotation.Qualifier; |
||||
import org.springframework.test.context.ContextCustomizer; |
||||
import org.springframework.test.context.ContextCustomizerFactories; |
||||
import org.springframework.test.context.ContextCustomizerFactory; |
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Integration test which verifies support for {@link ContextCustomizerFactory} |
||||
* and {@link ContextCustomizer} when a custom factory is registered declaratively |
||||
* via {@link ContextCustomizerFactories @ContextCustomizerFactories}. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 6.1 |
||||
*/ |
||||
@SpringJUnitConfig({}) |
||||
@CustomizeWithFruit |
||||
@CustomizeWithFoo |
||||
@ContextCustomizerFactories(EnigmaContextCustomizerFactory.class) |
||||
@CustomizeWithBar |
||||
class ContextCustomizerDeclarativeRegistrationTests { |
||||
|
||||
// GlobalFruitContextCustomizerFactory is registered via spring.factories
|
||||
@Autowired(required = false) |
||||
@Qualifier("global$fruit") |
||||
String fruit; |
||||
|
||||
@Autowired |
||||
Integer enigma; |
||||
|
||||
@Autowired(required = false) |
||||
@Qualifier("foo") |
||||
String foo; |
||||
|
||||
@Autowired(required = false) |
||||
@Qualifier("bar") |
||||
String bar; |
||||
|
||||
|
||||
@Test |
||||
void injectedBean() { |
||||
// registered globally via spring.factories
|
||||
assertThat(fruit).isEqualTo("apple, banana, cherry"); |
||||
|
||||
// From local @ContextCustomizerFactories
|
||||
assertThat(enigma).isEqualTo(42); |
||||
|
||||
// @ContextCustomizerFactories is not currently supported as a repeatable annotation,
|
||||
// and a directly present @ContextCustomizerFactories annotation overrides
|
||||
// @ContextCustomizerFactories meta-annotations.
|
||||
assertThat(foo).isNull(); |
||||
assertThat(bar).isNull(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,94 @@
@@ -0,0 +1,94 @@
|
||||
/* |
||||
* Copyright 2002-2023 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.customizers; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.beans.factory.annotation.Qualifier; |
||||
import org.springframework.test.context.BootstrapWith; |
||||
import org.springframework.test.context.ContextCustomizer; |
||||
import org.springframework.test.context.ContextCustomizerFactories; |
||||
import org.springframework.test.context.ContextCustomizerFactory; |
||||
import org.springframework.test.context.customizers.ContextCustomizerTests.EnigmaTestContextBootstrapper; |
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; |
||||
import org.springframework.test.context.support.AbstractTestContextBootstrapper; |
||||
import org.springframework.test.context.support.DefaultTestContextBootstrapper; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Integration test which verifies support for {@link ContextCustomizerFactory} |
||||
* and {@link ContextCustomizer} when a custom factory is registered by overriding |
||||
* {@link AbstractTestContextBootstrapper#getContextCustomizerFactories} and |
||||
* additional factories are registered declaratively via |
||||
* {@link ContextCustomizerFactories @ContextCustomizerFactories}. |
||||
* |
||||
* @author Sam Brannen |
||||
* @author Phillip Webb |
||||
* @since 4.3 |
||||
*/ |
||||
@SpringJUnitConfig({}) |
||||
@CustomizeWithFoo |
||||
@BootstrapWith(EnigmaTestContextBootstrapper.class) |
||||
@CustomizeWithBar |
||||
class ContextCustomizerTests { |
||||
|
||||
// GlobalFruitContextCustomizerFactory is registered via spring.factories
|
||||
@Autowired(required = false) |
||||
@Qualifier("global$fruit") |
||||
String fruit; |
||||
|
||||
@Autowired |
||||
Integer enigma; |
||||
|
||||
@Autowired(required = false) |
||||
@Qualifier("foo") |
||||
String foo; |
||||
|
||||
@Autowired(required = false) |
||||
@Qualifier("bar") |
||||
String bar; |
||||
|
||||
|
||||
@Test |
||||
void injectedBean() { |
||||
// Local Bootstrapper overrides spring.factories lookup
|
||||
assertThat(fruit).isNull(); |
||||
|
||||
// From local Bootstrapper
|
||||
assertThat(enigma).isEqualTo(42); |
||||
|
||||
// From local @ContextCustomizerFactories
|
||||
assertThat(foo).isEqualTo("bar"); |
||||
|
||||
// @ContextCustomizerFactories is not currently supported as a repeatable annotation.
|
||||
assertThat(bar).isNull(); |
||||
} |
||||
|
||||
|
||||
static class EnigmaTestContextBootstrapper extends DefaultTestContextBootstrapper { |
||||
|
||||
@Override |
||||
protected List<ContextCustomizerFactory> getContextCustomizerFactories() { |
||||
return List.of(new EnigmaContextCustomizerFactory()); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
/* |
||||
* Copyright 2002-2023 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.customizers; |
||||
|
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
import org.springframework.test.context.ContextCustomizerFactories; |
||||
|
||||
/** |
||||
* @author Sam Brannen |
||||
* @since 6.1 |
||||
*/ |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target(ElementType.TYPE) |
||||
@ContextCustomizerFactories(BarContextCustomizerFactory.class) |
||||
@interface CustomizeWithBar { |
||||
} |
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
/* |
||||
* Copyright 2002-2023 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.customizers; |
||||
|
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
import org.springframework.test.context.ContextCustomizerFactories; |
||||
|
||||
/** |
||||
* @author Sam Brannen |
||||
* @since 6.1 |
||||
*/ |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target(ElementType.TYPE) |
||||
@ContextCustomizerFactories(FooContextCustomizerFactory.class) |
||||
@interface CustomizeWithFoo { |
||||
} |
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
/* |
||||
* Copyright 2002-2023 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.customizers; |
||||
|
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Inherited; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
/** |
||||
* Marker annotation to trigger {@link GlobalFruitContextCustomizerFactory}. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 6.1 |
||||
*/ |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target(ElementType.TYPE) |
||||
@Inherited |
||||
@interface CustomizeWithFruit { |
||||
} |
@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
|
||||
/* |
||||
* Copyright 2002-2023 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.customizers; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.springframework.test.context.ContextConfigurationAttributes; |
||||
import org.springframework.test.context.ContextCustomizer; |
||||
import org.springframework.test.context.ContextCustomizerFactory; |
||||
|
||||
/** |
||||
* @author Sam Brannen |
||||
* @since 6.1 |
||||
*/ |
||||
class EnigmaContextCustomizerFactory implements ContextCustomizerFactory { |
||||
|
||||
@Override |
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass, |
||||
List<ContextConfigurationAttributes> configAttributes) { |
||||
return (context, mergedConfig) -> context.getBeanFactory().registerSingleton("enigma", 42); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
|
||||
/* |
||||
* Copyright 2002-2023 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.customizers; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.springframework.test.context.ContextConfigurationAttributes; |
||||
import org.springframework.test.context.ContextCustomizer; |
||||
import org.springframework.test.context.ContextCustomizerFactory; |
||||
|
||||
/** |
||||
* @author Sam Brannen |
||||
* @since 6.1 |
||||
*/ |
||||
class FooContextCustomizerFactory implements ContextCustomizerFactory { |
||||
|
||||
@Override |
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass, |
||||
List<ContextConfigurationAttributes> configAttributes) { |
||||
return (context, mergedConfig) -> context.getBeanFactory().registerSingleton("foo", "bar"); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,65 @@
@@ -0,0 +1,65 @@
|
||||
/* |
||||
* Copyright 2002-2023 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.customizers; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.test.context.ContextConfigurationAttributes; |
||||
import org.springframework.test.context.ContextCustomizer; |
||||
import org.springframework.test.context.ContextCustomizerFactory; |
||||
import org.springframework.test.context.MergedContextConfiguration; |
||||
|
||||
/** |
||||
* @author Sam Brannen |
||||
* @since 6.1 |
||||
*/ |
||||
class GlobalFruitContextCustomizerFactory implements ContextCustomizerFactory { |
||||
|
||||
@Override |
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass, |
||||
List<ContextConfigurationAttributes> configAttributes) { |
||||
|
||||
if (testClass.isAnnotationPresent(CustomizeWithFruit.class)) { |
||||
return new GlobalFruitContextCustomizer(); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
} |
||||
|
||||
class GlobalFruitContextCustomizer implements ContextCustomizer { |
||||
|
||||
@Override |
||||
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) { |
||||
// Use "global$fruit" as the bean name instead of something simple like "fruit"
|
||||
// to avoid bean name clashes with any test that registers a bean named "fruit".
|
||||
context.getBeanFactory().registerSingleton("global$fruit", "apple, banana, cherry"); |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(@Nullable Object other) { |
||||
return (this == other || (other != null && getClass() == other.getClass())); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return getClass().hashCode(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,46 @@
@@ -0,0 +1,46 @@
|
||||
/* |
||||
* Copyright 2002-2023 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.customizers; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.beans.factory.annotation.Qualifier; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* @author Sam Brannen |
||||
* @since 6.1 |
||||
*/ |
||||
@CustomizeWithBar |
||||
class InheritedContextCustomizerRegistrationTests extends LocalContextCustomizerRegistrationTests { |
||||
|
||||
@Autowired |
||||
@Qualifier("bar") |
||||
String bar; |
||||
|
||||
|
||||
@Override |
||||
@Test |
||||
void injectedBean() { |
||||
assertThat(fruit).isEqualTo("apple, banana, cherry"); |
||||
assertThat(foo).isEqualTo("bar"); |
||||
assertThat(bar).isEqualTo("baz"); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
/* |
||||
* Copyright 2002-2023 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.customizers; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.beans.factory.annotation.Qualifier; |
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* @author Sam Brannen |
||||
* @since 6.1 |
||||
*/ |
||||
@SpringJUnitConfig({}) |
||||
@CustomizeWithFruit |
||||
@CustomizeWithFoo |
||||
class LocalContextCustomizerRegistrationTests { |
||||
|
||||
// GlobalFruitContextCustomizerFactory is registered via spring.factories
|
||||
@Autowired(required = false) |
||||
@Qualifier("global$fruit") |
||||
String fruit; |
||||
|
||||
@Autowired(required = false) |
||||
@Qualifier("foo") |
||||
String foo; |
||||
|
||||
|
||||
@Test |
||||
void injectedBean() { |
||||
assertThat(fruit).isEqualTo("apple, banana, cherry"); |
||||
assertThat(foo).isEqualTo("bar"); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
|
||||
/* |
||||
* Copyright 2002-2023 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.customizers; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.beans.factory.annotation.Qualifier; |
||||
import org.springframework.test.context.ContextCustomizerFactories; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* @author Sam Brannen |
||||
* @since 6.1 |
||||
*/ |
||||
@ContextCustomizerFactories(factories = {BarContextCustomizerFactory.class, EnigmaContextCustomizerFactory.class}, |
||||
inheritFactories = false) |
||||
class OverriddenContextCustomizerRegistrationTests extends LocalContextCustomizerRegistrationTests { |
||||
|
||||
@Autowired |
||||
@Qualifier("bar") |
||||
String bar; |
||||
|
||||
@Autowired |
||||
Integer enigma; |
||||
|
||||
|
||||
@Override |
||||
@Test |
||||
void injectedBean() { |
||||
// globally registered via spring.factories
|
||||
assertThat(fruit).isEqualTo("apple, banana, cherry"); |
||||
|
||||
// Overridden by this subclass (inheritFactories = false)
|
||||
assertThat(foo).isNull(); |
||||
|
||||
// Local to this subclass
|
||||
assertThat(bar).isEqualTo("baz"); |
||||
assertThat(enigma).isEqualTo(42); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
|
||||
/* |
||||
* Copyright 2002-2023 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.customizers; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.beans.factory.annotation.Qualifier; |
||||
import org.springframework.test.context.ContextCustomizerFactories; |
||||
import org.springframework.test.context.ContextCustomizerFactories.MergeMode; |
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* @author Sam Brannen |
||||
* @since 6.1 |
||||
*/ |
||||
@SpringJUnitConfig({}) |
||||
@ContextCustomizerFactories(factories = {FooContextCustomizerFactory.class, BarContextCustomizerFactory.class}, |
||||
mergeMode = MergeMode.REPLACE_DEFAULTS) |
||||
class ReplaceDefaultsContextCustomizerTests { |
||||
|
||||
// GlobalFruitContextCustomizerFactory is registered via spring.factories
|
||||
@Autowired(required = false) |
||||
@Qualifier("global$fruit") |
||||
String fruit; |
||||
|
||||
@Autowired(required = false) |
||||
@Qualifier("foo") |
||||
String foo; |
||||
|
||||
@Autowired(required = false) |
||||
@Qualifier("bar") |
||||
String bar; |
||||
|
||||
|
||||
@Test |
||||
void injectedBean() { |
||||
// MergeMode.REPLACE_DEFAULTS overrides spring.factories lookup
|
||||
assertThat(fruit).isNull(); |
||||
|
||||
// From local @ContextCustomizerFactories
|
||||
assertThat(foo).isEqualTo("bar"); |
||||
assertThat(bar).isEqualTo("baz"); |
||||
} |
||||
|
||||
} |
@ -1,66 +0,0 @@
@@ -1,66 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2019 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.junit4; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.test.context.BootstrapWith; |
||||
import org.springframework.test.context.ContextCustomizer; |
||||
import org.springframework.test.context.ContextCustomizerFactory; |
||||
import org.springframework.test.context.junit4.ContextCustomizerSpringRunnerTests.CustomTestContextBootstrapper; |
||||
import org.springframework.test.context.support.DefaultTestContextBootstrapper; |
||||
|
||||
import static java.util.Collections.singletonList; |
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* JUnit 4 based integration test which verifies support of |
||||
* {@link ContextCustomizerFactory} and {@link ContextCustomizer}. |
||||
* |
||||
* @author Sam Brannen |
||||
* @author Phillip Webb |
||||
* @since 4.3 |
||||
*/ |
||||
@RunWith(SpringRunner.class) |
||||
@BootstrapWith(CustomTestContextBootstrapper.class) |
||||
public class ContextCustomizerSpringRunnerTests { |
||||
|
||||
@Autowired String foo; |
||||
|
||||
|
||||
@Test |
||||
public void injectedBean() { |
||||
assertThat(foo).isEqualTo("foo"); |
||||
} |
||||
|
||||
|
||||
static class CustomTestContextBootstrapper extends DefaultTestContextBootstrapper { |
||||
|
||||
@Override |
||||
protected List<ContextCustomizerFactory> getContextCustomizerFactories() { |
||||
return singletonList( |
||||
(ContextCustomizerFactory) (testClass, configAttributes) -> |
||||
(ContextCustomizer) (context, mergedConfig) -> context.getBeanFactory().registerSingleton("foo", "foo") |
||||
); |
||||
} |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue