Polish "Bean Overriding in Tests" support
This commit is contained in:
parent
ce8758b83c
commit
8727d723f3
|
@ -134,13 +134,13 @@
|
|||
**** xref:testing/testcontext-framework/ctx-management/failure-threshold.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/hierarchies.adoc[]
|
||||
*** xref:testing/testcontext-framework/fixture-di.adoc[]
|
||||
*** xref:testing/testcontext-framework/bean-overriding.adoc[]
|
||||
*** xref:testing/testcontext-framework/web-scoped-beans.adoc[]
|
||||
*** xref:testing/testcontext-framework/tx.adoc[]
|
||||
*** xref:testing/testcontext-framework/executing-sql.adoc[]
|
||||
*** xref:testing/testcontext-framework/parallel-test-execution.adoc[]
|
||||
*** xref:testing/testcontext-framework/support-classes.adoc[]
|
||||
*** xref:testing/testcontext-framework/aot.adoc[]
|
||||
*** xref:testing/testcontext-framework/bean-overriding.adoc[]
|
||||
** xref:testing/webtestclient.adoc[]
|
||||
** xref:testing/spring-mvc-test-framework.adoc[]
|
||||
*** xref:testing/spring-mvc-test-framework/server.adoc[]
|
||||
|
@ -172,6 +172,8 @@
|
|||
***** xref:testing/annotations/integration-spring/annotation-activeprofiles.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-testpropertysource.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-dynamicpropertysource.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-testbean.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-mockitobean.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-dirtiescontext.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-testexecutionlisteners.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-recordapplicationevents.adoc[]
|
||||
|
@ -184,8 +186,6 @@
|
|||
***** xref:testing/annotations/integration-spring/annotation-sqlmergemode.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-sqlgroup.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-disabledinaotmode.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-testbean.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-mockitobean.adoc[]
|
||||
**** xref:testing/annotations/integration-junit4.adoc[]
|
||||
**** xref:testing/annotations/integration-junit-jupiter.adoc[]
|
||||
**** xref:testing/annotations/integration-meta.adoc[]
|
||||
|
|
|
@ -16,6 +16,8 @@ Spring's testing annotations include the following:
|
|||
* xref:testing/annotations/integration-spring/annotation-activeprofiles.adoc[`@ActiveProfiles`]
|
||||
* xref:testing/annotations/integration-spring/annotation-testpropertysource.adoc[`@TestPropertySource`]
|
||||
* xref:testing/annotations/integration-spring/annotation-dynamicpropertysource.adoc[`@DynamicPropertySource`]
|
||||
* xref:testing/annotations/integration-spring/annotation-testbean.adoc[`@TestBean`]
|
||||
* xref:testing/annotations/integration-spring/annotation-mockitobean.adoc[`@MockitoBean` and `@MockitoSpyBean`]
|
||||
* xref:testing/annotations/integration-spring/annotation-dirtiescontext.adoc[`@DirtiesContext`]
|
||||
* xref:testing/annotations/integration-spring/annotation-testexecutionlisteners.adoc[`@TestExecutionListeners`]
|
||||
* xref:testing/annotations/integration-spring/annotation-recordapplicationevents.adoc[`@RecordApplicationEvents`]
|
||||
|
@ -28,6 +30,4 @@ Spring's testing annotations include the following:
|
|||
* xref:testing/annotations/integration-spring/annotation-sqlmergemode.adoc[`@SqlMergeMode`]
|
||||
* xref:testing/annotations/integration-spring/annotation-sqlgroup.adoc[`@SqlGroup`]
|
||||
* xref:testing/annotations/integration-spring/annotation-disabledinaotmode.adoc[`@DisabledInAotMode`]
|
||||
* xref:testing/annotations/integration-spring/annotation-testbean.adoc[`@TestBean`]
|
||||
* xref:testing/annotations/integration-spring/annotation-mockitobean.adoc[`@MockitoBean` and `@MockitoSpyBean`]
|
||||
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
[[spring-testing-annotation-beanoverriding-mockitobean]]
|
||||
= `@MockitoBean` and `@MockitoSpyBean`
|
||||
|
||||
`@MockitoBean` and `@MockitoSpyBean` are used on a test class field to override a bean
|
||||
with a mocking and spying instance, respectively. In the later case, the original bean
|
||||
definition is not replaced but instead an early instance is captured and wrapped by the
|
||||
spy.
|
||||
`@MockitoBean` and `@MockitoSpyBean` are used on test class fields to override beans in
|
||||
the test's `ApplicationContext` with a Mockito mock or spy, respectively. In the latter
|
||||
case, the original bean definition is not replaced, but instead an early instance of the
|
||||
bean is captured and wrapped by the spy.
|
||||
|
||||
By default, the name of the bean to override is derived from the annotated field's name,
|
||||
but both annotations allows for a specific `name` to be provided. Each annotation also
|
||||
but both annotations allow for a specific `name` to be provided. Each annotation also
|
||||
defines Mockito-specific attributes to fine-tune the mocking details.
|
||||
|
||||
The `@MockitoBean` annotation uses the `CREATE_OR_REPLACE_DEFINITION`
|
||||
The `@MockitoBean` annotation uses the `REPLACE_OR_CREATE_DEFINITION`
|
||||
xref:testing/testcontext-framework/bean-overriding.adoc#spring-testing-beanoverriding-extending[strategy for test bean overriding].
|
||||
|
||||
The `@MockitoSpyBean` annotation uses the `WRAP_EARLY_BEAN`
|
||||
xref:testing/testcontext-framework/bean-overriding.adoc#spring-testing-beanoverriding-extending[strategy]
|
||||
The `@MockitoSpyBean` annotation uses the `WRAP_BEAN`
|
||||
xref:testing/testcontext-framework/bean-overriding.adoc#spring-testing-beanoverriding-extending[strategy],
|
||||
and the original instance is wrapped in a Mockito spy.
|
||||
|
||||
The following example shows how to configure the bean name for both `@MockitoBean` and
|
||||
`@MockitoSpyBean` annotations:
|
||||
The following example shows how to configure the bean name via `@MockitoBean` and
|
||||
`@MockitoSpyBean`:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
|
@ -27,6 +27,7 @@ Java::
|
|||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
class OverrideBeanTests {
|
||||
|
||||
@MockitoBean(name = "service1") // <1>
|
||||
private CustomService mockService;
|
||||
|
||||
|
@ -36,7 +37,7 @@ Java::
|
|||
// test case body...
|
||||
}
|
||||
----
|
||||
<1> Mark `mockService` as a Mockito mock override of bean `service1` in this test class
|
||||
<2> Mark `spyService` as a Mockito spy override of bean `service2` in this test class
|
||||
<3> Both fields will be injected with the Mockito values (the mock and the spy respectively)
|
||||
======
|
||||
<1> Mark `mockService` as a Mockito mock override of bean `service1` in this test class.
|
||||
<2> Mark `spyService` as a Mockito spy override of bean `service2` in this test class.
|
||||
<3> The fields will be injected with the Mockito mock and spy, respectively.
|
||||
======
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
[[spring-testing-annotation-beanoverriding-testbean]]
|
||||
= `@TestBean`
|
||||
|
||||
`@TestBean` is used on a test class field to override a specific bean with an instance
|
||||
provided by a conventionally named static factory method.
|
||||
`@TestBean` is used on a test class field to override a specific bean in the test's
|
||||
`ApplicationContext` with an instance provided by a conventionally named static factory
|
||||
method.
|
||||
|
||||
By default, the bean name and the associated factory method name are derived from the
|
||||
annotated field's name but the annotation allows for specific values to be provided.
|
||||
annotated field's name, but the annotation allows for specific values to be provided.
|
||||
|
||||
The `@TestBean` annotation uses the `REPLACE_DEFINITION`
|
||||
xref:testing/testcontext-framework/bean-overriding.adoc#spring-testing-beanoverriding-extending[strategy for test bean overriding].
|
||||
|
||||
The following example shows how to fully configure the `@TestBean` annotation, with
|
||||
explicit values equivalent to the default:
|
||||
explicit values equivalent to the defaults:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
|
@ -30,10 +31,9 @@ Java::
|
|||
}
|
||||
}
|
||||
----
|
||||
<1> Mark a field for bean overriding in this test class
|
||||
<2> The result of this static method will be used as the instance and injected into the field
|
||||
<1> Mark a field for bean overriding in this test class.
|
||||
<2> The result of this static method will be used as the instance and injected into the field.
|
||||
======
|
||||
|
||||
NOTE: The method to invoke is searched in the test class and any enclosing class it might
|
||||
have, as well as its hierarchy. This typically allows nested test class to rely on the
|
||||
factory method in the root test class.
|
||||
NOTE: Spring searches for the factory method to invoke in the test class, in the test
|
||||
class hierarchy, and in the enclosing class hierarchy for a `@Nested` test class.
|
||||
|
|
|
@ -1,53 +1,67 @@
|
|||
[[spring-testing-beanoverriding]]
|
||||
[[testcontext-bean-overriding]]
|
||||
= Bean Overriding in Tests
|
||||
|
||||
Bean Overriding in Tests refers to the ability to override specific beans in the Context
|
||||
for a test class, by annotating one or more fields in said test class.
|
||||
Bean overriding in tests refers to the ability to override specific beans in the
|
||||
`ApplicationContext` for a test class, by annotating one or more fields in the test class.
|
||||
|
||||
NOTE: This is intended as a less risky alternative to the practice of registering a bean via
|
||||
`@Bean` with the `DefaultListableBeanFactory` `setAllowBeanDefinitionOverriding` set to
|
||||
`true`.
|
||||
NOTE: This feature is intended as a less risky alternative to the practice of registering
|
||||
a bean via `@Bean` with the `DefaultListableBeanFactory`
|
||||
`setAllowBeanDefinitionOverriding` flag set to `true`.
|
||||
|
||||
The Spring Testing Framework provides two sets of annotations:
|
||||
xref:testing/annotations/integration-spring/annotation-testbean.adoc[`@TestBean`],
|
||||
xref:testing/annotations/integration-spring/annotation-mockitobean.adoc[`@MockitoBean` and
|
||||
`@MockitoSpyBean`]. The former relies purely on Spring, while the later set relies on
|
||||
the https://site.mockito.org/[Mockito] third party library.
|
||||
The Spring TestContext framework provides two sets of annotations for bean overriding.
|
||||
|
||||
[[spring-testing-beanoverriding-extending]]
|
||||
== Extending bean override with a custom annotation
|
||||
* xref:testing/annotations/integration-spring/annotation-testbean.adoc[`@TestBean`]
|
||||
* xref:testing/annotations/integration-spring/annotation-mockitobean.adoc[`@MockitoBean` and `@MockitoSpyBean`]
|
||||
|
||||
The three annotations mentioned above build upon the `@BeanOverride` meta-annotation
|
||||
and associated infrastructure, which allows to define custom bean overriding variants.
|
||||
The former relies purely on Spring, while the latter set relies on the
|
||||
https://site.mockito.org/[Mockito] third-party library.
|
||||
|
||||
To create an extension, the following is needed:
|
||||
[[testcontext-bean-overriding-custom]]
|
||||
== Custom Bean Override Support
|
||||
|
||||
- An annotation meta-annotated with `@BeanOverride` that defines the
|
||||
`BeanOverrideProcessor` to use.
|
||||
- The `BeanOverrideProcessor` implementation itself.
|
||||
- One or more concrete `OverrideMetadata` implementations provided by the processor.
|
||||
The three annotations mentioned above build upon the `@BeanOverride` meta-annotation and
|
||||
associated infrastructure, which allows one to define custom bean overriding variants.
|
||||
|
||||
The Spring TestContext Framework includes infrastructure classes that support bean
|
||||
overriding: a `BeanFactoryPostProcessor`, a `TestExecutionListener` and a
|
||||
`ContextCustomizerFactory`.
|
||||
The later two are automatically registered via the Spring TestContext Framework
|
||||
`spring.factories` file, and are responsible for setting up the rest of the infrastructure.
|
||||
To create custom bean override support, the following is needed:
|
||||
|
||||
The test classes are parsed looking for any field meta-annotated with `@BeanOverride`,
|
||||
instantiating the relevant `BeanOverrideProcessor` in order to register an
|
||||
`OverrideMetadata`.
|
||||
* An annotation meta-annotated with `@BeanOverride` that defines the
|
||||
`BeanOverrideProcessor` to use
|
||||
* A custom `BeanOverrideProcessor` implementation
|
||||
* One or more concrete `OverrideMetadata` implementations provided by the processor
|
||||
|
||||
Then the `BeanOverrideBeanFactoryPostProcessor` will use that information to alter the
|
||||
context, registering and replacing bean definitions as defined by each metadata
|
||||
`BeanOverrideStrategy`:
|
||||
The Spring TestContext framework includes implementations of the following APIs that
|
||||
support bean overriding and are responsible for setting up the rest of the infrastructure.
|
||||
|
||||
- `REPLACE_DEFINITION`: replaces the bean definition. If it is not present in the
|
||||
context, an exception is thrown.
|
||||
- `CREATE_OR_REPLACE_DEFINITION`: replaces the bean definition if the bean definition
|
||||
does not exist, or create one if it is not.
|
||||
- `WRAP_BEAN`: get the original instance early so that it can be wrapped.
|
||||
* a `BeanFactoryPostProcessor`
|
||||
* a `ContextCustomizerFactory`
|
||||
* a `TestExecutionListener`
|
||||
|
||||
NOTE: The Bean Overriding infrastructure does not include any bean resolution step,
|
||||
unlike an `@Autowired`-annotated field for instance. As such, the name of the bean to
|
||||
override must be somehow provided to or computed by the `BeanOverrideProcessor`.
|
||||
Typically, the user provides the name one way or the other.
|
||||
The `spring-test` module registers implementations of the latter two
|
||||
(`BeanOverrideContextCustomizerFactory` and `BeanOverrideTestExecutionListener`) in its
|
||||
{spring-framework-code}/spring-test/src/main/resources/META-INF/spring.factories[`META-INF/spring.factories`
|
||||
properties file].
|
||||
|
||||
The bean overriding infrastructure searches in test classes for any field meta-annotated
|
||||
with `@BeanOverride` and instantiates the corresponding `BeanOverrideProcessor` which is
|
||||
responsible for registering appropriate `OverrideMetadata`.
|
||||
|
||||
The internal `BeanOverrideBeanFactoryPostProcessor` then uses that information to alter
|
||||
the test's `ApplicationContext` by registering and replacing bean definitions as defined
|
||||
by the corresponding `BeanOverrideStrategy`:
|
||||
|
||||
* `REPLACE_DEFINITION`: Replaces the bean definition. Throws an exception if a
|
||||
corresponding bean definition does not exist.
|
||||
* `REPLACE_OR_CREATE_DEFINITION`: Replaces the bean definition if it exists. Creates a
|
||||
new bean definition if a corresponding bean definition does not exist.
|
||||
* `WRAP_BEAN`: Retrieves the original bean instance and wraps it.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
In contrast to Spring's autowiring mechanism (for example, resolution of an `@Autowired`
|
||||
field), the bean overriding infrastructure in the TestContext framework does not perform
|
||||
any heuristics to locate a bean. Instead, the name of the bean to override must be
|
||||
explicitly provided to or computed by the `BeanOverrideProcessor`.
|
||||
|
||||
Typically, the user provides the bean name via a custom annotation, or the
|
||||
`BeanOverrideProcessor` determines the bean name based on some convention.
|
||||
====
|
||||
|
|
|
@ -22,25 +22,23 @@ import java.lang.annotation.RetentionPolicy;
|
|||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Mark an annotation as eligible for Bean Override processing.
|
||||
* Mark a composed annotation as eligible for Bean Override processing.
|
||||
*
|
||||
* <p>Specifying this annotation triggers the defined {@link BeanOverrideProcessor}
|
||||
* <p>Specifying this annotation triggers the configured {@link BeanOverrideProcessor}
|
||||
* which must be capable of handling the composed annotation and its attributes.
|
||||
*
|
||||
* <p>The composed annotation is meant to be detected on fields only so it is
|
||||
* expected that it has a {@code Target} of {@link ElementType#FIELD FIELD}.
|
||||
* <p>Since the composed annotation should only be applied to fields, it is
|
||||
* expected that it has a {@link Target} of {@link ElementType#FIELD FIELD}.
|
||||
*
|
||||
* @author Simon Baslé
|
||||
* @since 6.2
|
||||
* @see BeanOverrideBeanFactoryPostProcessor
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
public @interface BeanOverride {
|
||||
|
||||
/**
|
||||
* The {@link BeanOverrideProcessor} implementation to trigger against
|
||||
* the composed annotation.
|
||||
* The {@link BeanOverrideProcessor} implementation to use.
|
||||
*/
|
||||
Class<? extends BeanOverrideProcessor> value();
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ import org.springframework.util.StringUtils;
|
|||
|
||||
/**
|
||||
* A {@link BeanFactoryPostProcessor} implementation that processes test classes
|
||||
* and adapt the {@link BeanFactory} for any {@link BeanOverride} it may define.
|
||||
* and adapts the {@link BeanFactory} for any {@link BeanOverride} it may define.
|
||||
*
|
||||
* <p>A set of classes from which to parse {@link OverrideMetadata} must be
|
||||
* provided to this processor. Each test class is expected to use any
|
||||
|
@ -51,7 +51,7 @@ import org.springframework.util.StringUtils;
|
|||
*
|
||||
* <p>The provided classes are fully parsed at creation to build a metadata set.
|
||||
* This processor implements several {@link BeanOverrideStrategy overriding
|
||||
* strategy} and chooses the correct one according to each override metadata
|
||||
* strategies} and chooses the correct one according to each override metadata's
|
||||
* {@link OverrideMetadata#getStrategy()} method. Additionally, it provides
|
||||
* support for injecting the overridden bean instances into their corresponding
|
||||
* annotated {@link Field fields}.
|
||||
|
@ -61,7 +61,6 @@ import org.springframework.util.StringUtils;
|
|||
*/
|
||||
class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered {
|
||||
|
||||
|
||||
private final BeanOverrideRegistrar overrideRegistrar;
|
||||
|
||||
|
||||
|
@ -195,14 +194,13 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
|
|||
static final class WrapEarlyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor,
|
||||
PriorityOrdered {
|
||||
|
||||
private final BeanOverrideRegistrar overrideRegistrar;
|
||||
private final Map<String, Object> earlyReferences = new ConcurrentHashMap<>(16);
|
||||
|
||||
private final Map<String, Object> earlyReferences;
|
||||
private final BeanOverrideRegistrar overrideRegistrar;
|
||||
|
||||
|
||||
private WrapEarlyBeanPostProcessor(BeanOverrideRegistrar registrar) {
|
||||
this.overrideRegistrar = registrar;
|
||||
this.earlyReferences = new ConcurrentHashMap<>(16);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ class BeanOverrideContextCustomizer implements ContextCustomizer {
|
|||
|
||||
private static void addInfrastructureBeanDefinition(BeanDefinitionRegistry registry,
|
||||
Class<?> clazz, String beanName, Consumer<ConstructorArgumentValues> constructorArgumentsConsumer) {
|
||||
|
||||
if (!registry.containsBeanDefinition(beanName)) {
|
||||
RootBeanDefinition definition = new RootBeanDefinition(clazz);
|
||||
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
|
|
|
@ -84,12 +84,11 @@ abstract class BeanOverrideParsingUtils {
|
|||
private static void parseField(Field field, Class<?> testClass, Set<OverrideMetadata> metadataSet) {
|
||||
AtomicBoolean overrideAnnotationFound = new AtomicBoolean();
|
||||
MergedAnnotations.from(field, DIRECT).stream(BeanOverride.class).forEach(mergedAnnotation -> {
|
||||
Assert.state(mergedAnnotation.isMetaPresent(), "@BeanOverride annotation must be meta-present");
|
||||
MergedAnnotation<?> metaSource = mergedAnnotation.getMetaSource();
|
||||
Assert.state(metaSource != null, "@BeanOverride annotation must be meta-present");
|
||||
|
||||
BeanOverride beanOverride = mergedAnnotation.synthesize();
|
||||
BeanOverrideProcessor processor = BeanUtils.instantiateClass(beanOverride.value());
|
||||
MergedAnnotation<?> metaSource = mergedAnnotation.getMetaSource();
|
||||
Assert.state(metaSource != null, "Meta-annotation source must not be null");
|
||||
Annotation composedAnnotation = metaSource.synthesize();
|
||||
|
||||
Assert.state(overrideAnnotationFound.compareAndSet(false, true),
|
||||
|
|
|
@ -20,11 +20,11 @@ import java.lang.annotation.Annotation;
|
|||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* Strategy interface for Bean Override processing, providing an
|
||||
* Strategy interface for Bean Override processing, providing
|
||||
* {@link OverrideMetadata} that drives how the target bean is overridden.
|
||||
*
|
||||
* <p>At least one composed annotations meta-annotated with
|
||||
* {@link BeanOverride @BeanOverride}) is a companion of this processor and
|
||||
* <p>At least one composed annotation that is meta-annotated with
|
||||
* {@link BeanOverride @BeanOverride} must be a companion of this processor and
|
||||
* may provide additional user settings that drive how the concrete
|
||||
* {@link OverrideMetadata} is configured.
|
||||
*
|
||||
|
|
|
@ -41,23 +41,22 @@ import org.springframework.util.StringUtils;
|
|||
*/
|
||||
class BeanOverrideRegistrar implements BeanFactoryAware {
|
||||
|
||||
private final Map<OverrideMetadata, String> beanNameRegistry;
|
||||
private final Map<OverrideMetadata, String> beanNameRegistry = new HashMap<>();
|
||||
|
||||
private final Map<String, OverrideMetadata> earlyOverrideMetadata;
|
||||
private final Map<String, OverrideMetadata> earlyOverrideMetadata = new HashMap<>();
|
||||
|
||||
private final Set<OverrideMetadata> overrideMetadata;
|
||||
|
||||
@Nullable
|
||||
private ConfigurableBeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new registrar and immediately parse the provided classes.
|
||||
* @param classesToParse the initial set of classes that have been
|
||||
* detected to contain bean overriding annotations
|
||||
*/
|
||||
BeanOverrideRegistrar(Set<Class<?>> classesToParse) {
|
||||
this.beanNameRegistry = new HashMap<>();
|
||||
this.earlyOverrideMetadata = new HashMap<>();
|
||||
this.overrideMetadata = BeanOverrideParsingUtils.parse(classesToParse);
|
||||
}
|
||||
|
||||
|
@ -71,7 +70,7 @@ class BeanOverrideRegistrar implements BeanFactoryAware {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the detected {@link OverrideMetadata} instances.
|
||||
* Get the detected {@link OverrideMetadata} instances.
|
||||
*/
|
||||
Set<OverrideMetadata> getOverrideMetadata() {
|
||||
return this.overrideMetadata;
|
||||
|
@ -85,16 +84,16 @@ class BeanOverrideRegistrar implements BeanFactoryAware {
|
|||
Object wrapIfNecessary(Object bean, String beanName) throws BeansException {
|
||||
OverrideMetadata metadata = this.earlyOverrideMetadata.get(beanName);
|
||||
if (metadata != null && metadata.getStrategy() == BeanOverrideStrategy.WRAP_BEAN) {
|
||||
bean = metadata.createOverride(beanName, null, bean);
|
||||
Assert.state(this.beanFactory != null, "ConfigurableBeanFactory must not be null");
|
||||
bean = metadata.createOverride(beanName, null, bean);
|
||||
metadata.track(bean, this.beanFactory);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the provided {@link OverrideMetadata} and associate it with a
|
||||
* {@code beanName}.
|
||||
* Register the provided {@link OverrideMetadata} and associate it with the
|
||||
* supplied {@code beanName}.
|
||||
*/
|
||||
void registerNameForMetadata(OverrideMetadata metadata, String beanName) {
|
||||
this.beanNameRegistry.put(metadata, beanName);
|
||||
|
@ -119,7 +118,7 @@ class BeanOverrideRegistrar implements BeanFactoryAware {
|
|||
try {
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
Object existingValue = ReflectionUtils.getField(field, target);
|
||||
Assert.state(this.beanFactory != null, "beanFactory must not be null");
|
||||
Assert.state(this.beanFactory != null, "ConfigurableBeanFactory must not be null");
|
||||
Object bean = this.beanFactory.getBean(beanName, field.getType());
|
||||
if (existingValue == bean) {
|
||||
return;
|
||||
|
|
|
@ -35,7 +35,7 @@ public enum BeanOverrideStrategy {
|
|||
/**
|
||||
* Replace or create a given bean definition, immediately preparing a
|
||||
* singleton instance.
|
||||
* <p>Contrary to {@link #REPLACE_DEFINITION} this create a new bean
|
||||
* <p>Contrary to {@link #REPLACE_DEFINITION}, this creates a new bean
|
||||
* definition if the target bean definition does not exist rather than
|
||||
* failing.
|
||||
*/
|
||||
|
@ -43,8 +43,9 @@ public enum BeanOverrideStrategy {
|
|||
|
||||
/**
|
||||
* Intercept and process an early bean reference rather than a bean
|
||||
* definition, allowing variants of bean overriding to wrap the instance.
|
||||
* For instance, to delegate to actual methods in the context of a mocking "spy".
|
||||
* definition, allowing variants of bean overriding to wrap the instance
|
||||
* — for example, to delegate to actual methods in the context of a
|
||||
* mocking "spy".
|
||||
*/
|
||||
WRAP_BEAN
|
||||
|
||||
|
|
|
@ -20,15 +20,13 @@ import java.lang.reflect.Field;
|
|||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.TestExecutionListener;
|
||||
import org.springframework.test.context.support.AbstractTestExecutionListener;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* {@link TestExecutionListener} implementation that enables Bean Override
|
||||
* support in tests, injecting overridden beans in appropriate fields of the
|
||||
* test instance.
|
||||
* {@code TestExecutionListener} that enables Bean Override support in tests,
|
||||
* injecting overridden beans in appropriate fields of the test instance.
|
||||
*
|
||||
* @author Simon Baslé
|
||||
* @since 6.2
|
||||
|
@ -54,9 +52,9 @@ public class BeanOverrideTestExecutionListener extends AbstractTestExecutionList
|
|||
}
|
||||
|
||||
/**
|
||||
* Process the test instance and make sure that flagged fields for bean
|
||||
* overriding are processed. Each field get is value updated with the
|
||||
* overridden bean instance.
|
||||
* Process the test instance and make sure that fields flagged for bean
|
||||
* overriding are processed.
|
||||
* <p>Each field's value will be updated with the overridden bean instance.
|
||||
*/
|
||||
protected void injectFields(TestContext testContext) {
|
||||
postProcessFields(testContext, (testMetadata, overrideRegistrar) -> overrideRegistrar.inject(
|
||||
|
@ -64,9 +62,10 @@ public class BeanOverrideTestExecutionListener extends AbstractTestExecutionList
|
|||
}
|
||||
|
||||
/**
|
||||
* Process the test instance and make sure that flagged fields for bean
|
||||
* overriding are processed. If a fresh instance is required, the field
|
||||
* is nulled out and then re-injected with the overridden bean instance.
|
||||
* Process the test instance and make sure that fields flagged for bean
|
||||
* overriding are processed.
|
||||
* <p>If a fresh instance is required, the field is nulled out and then
|
||||
* re-injected with the overridden bean instance.
|
||||
* <p>This method does nothing if the
|
||||
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE}
|
||||
* attribute is not present in the {@code TestContext}.
|
||||
|
|
|
@ -26,15 +26,15 @@ import org.springframework.core.style.ToStringCreator;
|
|||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Metadata for Bean Override injection points, also responsible for the
|
||||
* creation of the overriding instance.
|
||||
* Metadata for Bean Override injection points, also responsible for creation of
|
||||
* the overriding instance.
|
||||
*
|
||||
* <p><strong>WARNING</strong>: implementations are used as a cache key and
|
||||
* must implement proper {@code equals} and {@code hashCode}t methods.
|
||||
* must implement proper {@code equals()} and {@code hashCode()} methods.
|
||||
*
|
||||
* <p>Specific implementations of metadata can have state to be used during
|
||||
* override {@linkplain #createOverride(String, BeanDefinition, Object)
|
||||
* instance creation} — for example, from further parsing of the
|
||||
* instance creation} — for example, based on further parsing of the
|
||||
* annotation or the annotated field.
|
||||
*
|
||||
* @author Simon Baslé
|
||||
|
@ -58,31 +58,32 @@ public abstract class OverrideMetadata {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the bean name to override.
|
||||
* Get the bean name to override.
|
||||
* <p>Defaults to the name of the {@link #getField() field}.
|
||||
*/
|
||||
protected String getBeanName() {
|
||||
return this.field.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean {@link ResolvableType type} to override.
|
||||
* Get the bean {@linkplain ResolvableType type} to override.
|
||||
*/
|
||||
public ResolvableType getBeanType() {
|
||||
public final ResolvableType getBeanType() {
|
||||
return this.beanType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the annotated {@link Field}.
|
||||
* Get the annotated {@link Field}.
|
||||
*/
|
||||
public Field getField() {
|
||||
public final Field getField() {
|
||||
return this.field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link BeanOverrideStrategy} for this instance, as a hint on
|
||||
* Get the {@link BeanOverrideStrategy} for this instance, as a hint on
|
||||
* how and when the override instance should be created.
|
||||
*/
|
||||
public BeanOverrideStrategy getStrategy() {
|
||||
public final BeanOverrideStrategy getStrategy() {
|
||||
return this.strategy;
|
||||
}
|
||||
|
||||
|
@ -91,9 +92,9 @@ public abstract class OverrideMetadata {
|
|||
* optionally provided with an existing {@link BeanDefinition} and/or an
|
||||
* original instance, that is a singleton or an early wrapped instance.
|
||||
* @param beanName the name of the bean being overridden
|
||||
* @param existingBeanDefinition an existing bean definition for that bean
|
||||
* name, or {@code null} if not relevant
|
||||
* @param existingBeanInstance an existing instance for that bean name,
|
||||
* @param existingBeanDefinition an existing bean definition for the supplied
|
||||
* bean name, or {@code null} if not relevant
|
||||
* @param existingBeanInstance an existing instance for the supplied bean name
|
||||
* for wrapping purposes, or {@code null} if irrelevant
|
||||
* @return the instance with which to override the bean
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue