Rename OverrideMetadata for Bean Overrides

Prior to this commit, OverrideMetadata was the only public type in the
org.springframework.test.context.bean.override package whose name did
not start with BeanOverride. In addition, an OverrideMetadata component
plays multiple roles in addition to serving as a holder for metadata.

This commit therefore renames OverrideMetadata to BeanOverrideHandler.

In addition, this commit updates the affected documentation and renames
the following related methods in the Bean Override support.

- BeanOverrideHandler: createOverride() -> createOverrideInstance()
- BeanOverrideHandler: track() -> trackOverrideInstance()
- BeanOverrideProcessor: createMetadata() -> createHandler()
- BeanOverrideContextCustomizer: getMetadata() -> getBeanOverrideHandlers()
- BeanOverrideRegistrar: registerNameForMetadata() -> registerBeanOverrideHandler()
- BeanOverrideRegistrar: markWrapEarly() -> registerWrappingBeanOverrideHandler()

Closes gh-33702
This commit is contained in:
Sam Brannen 2024-10-16 16:32:38 +02:00
parent ab4fe5a29c
commit c0c78bd67e
28 changed files with 674 additions and 663 deletions

View File

@ -22,12 +22,12 @@ https://site.mockito.org/[Mockito] third-party library.
The three annotations mentioned above build upon the `@BeanOverride` meta-annotation and
associated infrastructure, which allows one to define custom bean overriding variants.
To create custom bean override support, the following is needed:
To implement custom bean override support, the following is needed:
* 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
* One or more concrete `BeanOverrideHandler` implementations created by the processor
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.
@ -43,11 +43,11 @@ 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`.
responsible for creating an appropriate `BeanOverrideHandler`.
The internal `BeanOverrideBeanFactoryPostProcessor` then uses that information to alter
the test's `ApplicationContext` by registering and replacing beans as defined by the
corresponding `BeanOverrideStrategy`:
The internal `BeanOverrideBeanFactoryPostProcessor` then uses bean override handlers to
alter the test's `ApplicationContext` by creating, replacing, or wrapping beans as
defined by the corresponding `BeanOverrideStrategy`:
`REPLACE`::
Replaces the bean. Throws an exception if a corresponding bean does not exist.
@ -65,11 +65,11 @@ heuristics it can perform to locate a bean. Either the `BeanOverrideProcessor` c
the name of the bean to override, or it can be unambiguously selected given the type of
the annotated field and its qualifying annotations.
Typically, the bean is selected by type by the `BeanOverrideFactoryPostProcessor`.
Typically, the bean is selected "by type" by the `BeanOverrideFactoryPostProcessor`.
Alternatively, the user can directly provide the bean name in the custom annotation.
Some `BeanOverrideProcessor` implementations could also internally compute a bean name
based on a convention or another advanced method.
`BeanOverrideProcessor` implementations may also internally compute a bean name based on
a convention or some other method.
====
NOTE: Only _singleton_ beans can be overridden. Any attempt to override a non-singleton

View File

@ -47,12 +47,12 @@ import org.springframework.util.Assert;
* accordingly.
*
* <p>For each override, the bean factory is prepared according to the chosen
* {@linkplain BeanOverrideStrategy override strategy}. The override value is created,
* if necessary, and the necessary infrastructure is updated to allow the value
* to be injected in the corresponding {@linkplain OverrideMetadata#getField() field}
* of the test class.
* {@linkplain BeanOverrideStrategy override strategy}. The bean override instance
* is created, if necessary, and the related infrastructure is updated to allow
* the bean override instance to be injected into the corresponding
* {@linkplain BeanOverrideHandler#getField() field} of the test class.
*
* <p>This processor does not work against a particular test class, but rather
* <p>This processor does not work against a particular test class but rather
* only prepares the bean factory for the identified, unique set of bean overrides.
*
* @author Simon Baslé
@ -66,22 +66,23 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
private static final BeanNameGenerator beanNameGenerator = DefaultBeanNameGenerator.INSTANCE;
private final Set<OverrideMetadata> metadata;
private final Set<BeanOverrideHandler> beanOverrideHandlers;
private final BeanOverrideRegistrar overrideRegistrar;
private final BeanOverrideRegistrar beanOverrideRegistrar;
/**
* Create a new {@code BeanOverrideBeanFactoryPostProcessor} with the supplied
* set of {@link OverrideMetadata} to process, using the given
* {@link BeanOverrideRegistrar}.
* @param metadata the {@link OverrideMetadata} instances to process
* @param overrideRegistrar the {@code BeanOverrideRegistrar} used to track
* metadata
* set of {@link BeanOverrideHandler BeanOverrideHandlers} to process, using
* the given {@link BeanOverrideRegistrar}.
* @param beanOverrideHandlers the bean override handlers to process
* @param beanOverrideRegistrar the registrar used to track bean override handlers
*/
BeanOverrideBeanFactoryPostProcessor(Set<OverrideMetadata> metadata, BeanOverrideRegistrar overrideRegistrar) {
this.metadata = metadata;
this.overrideRegistrar = overrideRegistrar;
BeanOverrideBeanFactoryPostProcessor(Set<BeanOverrideHandler> beanOverrideHandlers,
BeanOverrideRegistrar beanOverrideRegistrar) {
this.beanOverrideHandlers = beanOverrideHandlers;
this.beanOverrideRegistrar = beanOverrideRegistrar;
}
@ -92,27 +93,27 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
for (OverrideMetadata metadata : this.metadata) {
registerBeanOverride(beanFactory, metadata);
for (BeanOverrideHandler handler : this.beanOverrideHandlers) {
registerBeanOverride(beanFactory, handler);
}
}
private void registerBeanOverride(ConfigurableListableBeanFactory beanFactory, OverrideMetadata overrideMetadata) {
String beanName = overrideMetadata.getBeanName();
Field field = overrideMetadata.getField();
private void registerBeanOverride(ConfigurableListableBeanFactory beanFactory, BeanOverrideHandler handler) {
String beanName = handler.getBeanName();
Field field = handler.getField();
Assert.state(!BeanFactoryUtils.isFactoryDereference(beanName),() -> """
Unable to override bean '%s' for field '%s.%s': a FactoryBean cannot be overridden. \
To override the bean created by the FactoryBean, remove the '&' prefix.""".formatted(
beanName, field.getDeclaringClass().getSimpleName(), field.getName()));
switch (overrideMetadata.getStrategy()) {
case REPLACE -> replaceBean(beanFactory, overrideMetadata, true);
case REPLACE_OR_CREATE -> replaceBean(beanFactory, overrideMetadata, false);
case WRAP -> wrapBean(beanFactory, overrideMetadata);
switch (handler.getStrategy()) {
case REPLACE -> replaceOrCreateBean(beanFactory, handler, true);
case REPLACE_OR_CREATE -> replaceOrCreateBean(beanFactory, handler, false);
case WRAP -> wrapBean(beanFactory, handler);
}
}
private void replaceBean(ConfigurableListableBeanFactory beanFactory, OverrideMetadata overrideMetadata,
private void replaceOrCreateBean(ConfigurableListableBeanFactory beanFactory, BeanOverrideHandler handler,
boolean requireExistingBean) {
// NOTE: This method supports 3 distinct scenarios which must be accounted for.
@ -121,10 +122,10 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
// 2) AOT processing
// 3) AOT runtime
String beanName = overrideMetadata.getBeanName();
String beanName = handler.getBeanName();
BeanDefinition existingBeanDefinition = null;
if (beanName == null) {
beanName = getBeanNameForType(beanFactory, overrideMetadata, requireExistingBean);
beanName = getBeanNameForType(beanFactory, handler, requireExistingBean);
if (beanName != null) {
// We are overriding an existing bean by-type.
beanName = BeanFactoryUtils.transformedBeanName(beanName);
@ -141,7 +142,7 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
}
}
else {
Set<String> candidates = getExistingBeanNamesByType(beanFactory, overrideMetadata, false);
Set<String> candidates = getExistingBeanNamesByType(beanFactory, handler, false);
if (candidates.contains(beanName)) {
// We are overriding an existing bean by-name.
existingBeanDefinition = beanFactory.getBeanDefinition(beanName);
@ -150,7 +151,7 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
throw new IllegalStateException("""
Unable to override bean: there is no bean to replace \
with name [%s] and type [%s]."""
.formatted(beanName, overrideMetadata.getBeanType()));
.formatted(beanName, handler.getBeanType()));
}
}
@ -180,7 +181,7 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
"that doesn't implement BeanDefinitionRegistry: " + beanFactory.getClass().getName());
}
RootBeanDefinition pseudoBeanDefinition = createPseudoBeanDefinition(overrideMetadata);
RootBeanDefinition pseudoBeanDefinition = createPseudoBeanDefinition(handler);
// Generate a name for the nonexistent bean.
if (PSEUDO_BEAN_NAME_PLACEHOLDER.equals(beanName)) {
@ -190,9 +191,9 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
registry.registerBeanDefinition(beanName, pseudoBeanDefinition);
}
Object override = overrideMetadata.createOverride(beanName, existingBeanDefinition, null);
overrideMetadata.track(override, beanFactory);
this.overrideRegistrar.registerNameForMetadata(overrideMetadata, beanName);
Object override = handler.createOverrideInstance(beanName, existingBeanDefinition, null);
handler.trackOverrideInstance(override, beanFactory);
this.beanOverrideRegistrar.registerBeanOverrideHandler(handler, beanName);
// Now we have an instance (the override) that we can manually register as a singleton.
//
@ -202,7 +203,7 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
//
// As a bonus, by manually registering a singleton during "AOT processing", we allow
// GenericApplicationContext's preDetermineBeanType() method to transparently register
// runtime hints for a proxy generated by the above createOverride() invocation --
// runtime hints for a proxy generated by the above createOverrideInstance() invocation --
// for example, when @MockitoBean creates a mock based on a JDK dynamic proxy.
if (beanFactory.containsSingleton(beanName)) {
destroySingleton(beanFactory, beanName);
@ -211,73 +212,74 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
}
/**
* Check that the expected bean name is registered and matches the type to override.
* <p>If so, put the override metadata in the early tracking map.
* Check that a bean with the specified {@link BeanOverrideHandler#getBeanName() name}
* and {@link BeanOverrideHandler#getBeanType() type} is registered.
* <p>If so, put the {@link BeanOverrideHandler} in the early tracking map.
* <p>The map will later be checked to see if a given bean should be wrapped
* upon creation, during the {@link WrapEarlyBeanPostProcessor#getEarlyBeanReference(Object, String)}
* upon creation, during the {@link WrapEarlyBeanPostProcessor#getEarlyBeanReference}
* phase.
*/
private void wrapBean(ConfigurableListableBeanFactory beanFactory, OverrideMetadata overrideMetadata) {
String beanName = overrideMetadata.getBeanName();
private void wrapBean(ConfigurableListableBeanFactory beanFactory, BeanOverrideHandler handler) {
String beanName = handler.getBeanName();
if (beanName == null) {
Set<String> candidateNames = getExistingBeanNamesByType(beanFactory, overrideMetadata, true);
Set<String> candidateNames = getExistingBeanNamesByType(beanFactory, handler, true);
int candidateCount = candidateNames.size();
if (candidateCount != 1) {
Field field = overrideMetadata.getField();
Field field = handler.getField();
throw new IllegalStateException("""
Unable to select a bean to override by wrapping: found %d bean instances of type %s \
(as required by annotated field '%s.%s')%s"""
.formatted(candidateCount, overrideMetadata.getBeanType(),
.formatted(candidateCount, handler.getBeanType(),
field.getDeclaringClass().getSimpleName(), field.getName(),
(candidateCount > 0 ? ": " + candidateNames : "")));
}
beanName = BeanFactoryUtils.transformedBeanName(candidateNames.iterator().next());
}
else {
Set<String> candidates = getExistingBeanNamesByType(beanFactory, overrideMetadata, false);
Set<String> candidates = getExistingBeanNamesByType(beanFactory, handler, false);
if (!candidates.contains(beanName)) {
throw new IllegalStateException("""
Unable to override bean by wrapping: there is no existing bean \
with name [%s] and type [%s]."""
.formatted(beanName, overrideMetadata.getBeanType()));
.formatted(beanName, handler.getBeanType()));
}
}
validateBeanDefinition(beanFactory, beanName);
this.overrideRegistrar.markWrapEarly(overrideMetadata, beanName);
this.overrideRegistrar.registerNameForMetadata(overrideMetadata, beanName);
this.beanOverrideRegistrar.registerWrappingBeanOverrideHandler(handler, beanName);
this.beanOverrideRegistrar.registerBeanOverrideHandler(handler, beanName);
}
@Nullable
private String getBeanNameForType(ConfigurableListableBeanFactory beanFactory, OverrideMetadata overrideMetadata,
private String getBeanNameForType(ConfigurableListableBeanFactory beanFactory, BeanOverrideHandler handler,
boolean requireExistingBean) {
Set<String> candidateNames = getExistingBeanNamesByType(beanFactory, overrideMetadata, true);
Set<String> candidateNames = getExistingBeanNamesByType(beanFactory, handler, true);
int candidateCount = candidateNames.size();
if (candidateCount == 1) {
return candidateNames.iterator().next();
}
else if (candidateCount == 0) {
if (requireExistingBean) {
Field field = overrideMetadata.getField();
Field field = handler.getField();
throw new IllegalStateException(
"Unable to override bean: no beans of type %s (as required by annotated field '%s.%s')"
.formatted(overrideMetadata.getBeanType(), field.getDeclaringClass().getSimpleName(), field.getName()));
.formatted(handler.getBeanType(), field.getDeclaringClass().getSimpleName(), field.getName()));
}
return null;
}
Field field = overrideMetadata.getField();
Field field = handler.getField();
throw new IllegalStateException("""
Unable to select a bean to override: found %s beans of type %s \
(as required by annotated field '%s.%s'): %s"""
.formatted(candidateCount, overrideMetadata.getBeanType(), field.getDeclaringClass().getSimpleName(),
.formatted(candidateCount, handler.getBeanType(), field.getDeclaringClass().getSimpleName(),
field.getName(), candidateNames));
}
private Set<String> getExistingBeanNamesByType(ConfigurableListableBeanFactory beanFactory, OverrideMetadata metadata,
private Set<String> getExistingBeanNamesByType(ConfigurableListableBeanFactory beanFactory, BeanOverrideHandler handler,
boolean checkAutowiredCandidate) {
ResolvableType resolvableType = metadata.getBeanType();
ResolvableType resolvableType = handler.getBeanType();
Class<?> type = resolvableType.toClass();
// Start with matching bean names for type, excluding FactoryBeans.
@ -296,7 +298,7 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
// Filter out non-matching autowire candidates.
if (checkAutowiredCandidate) {
DependencyDescriptor descriptor = new DependencyDescriptor(metadata.getField(), true);
DependencyDescriptor descriptor = new DependencyDescriptor(handler.getField(), true);
beanNames.removeIf(beanName -> !beanFactory.isAutowireCandidate(beanName, descriptor));
}
// Filter out scoped proxy targets.
@ -304,7 +306,7 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
// In case of multiple matches, fall back on the field's name as a last resort.
if (beanNames.size() > 1) {
String fieldName = metadata.getField().getName();
String fieldName = handler.getField().getName();
if (beanNames.contains(fieldName)) {
return Set.of(fieldName);
}
@ -313,21 +315,21 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
}
/**
* Create a pseudo-{@link BeanDefinition} for the supplied {@link OverrideMetadata},
* Create a pseudo-{@link BeanDefinition} for the supplied {@link BeanOverrideHandler},
* whose {@linkplain RootBeanDefinition#getTargetType() target type} and
* {@linkplain RootBeanDefinition#getQualifiedElement() qualified element} are
* the {@linkplain OverrideMetadata#getBeanType() bean type} and
* the {@linkplain OverrideMetadata#getField() field} of the {@code OverrideMetadata},
* the {@linkplain BeanOverrideHandler#getBeanType() bean type} and
* the {@linkplain BeanOverrideHandler#getField() field} of the {@code BeanOverrideHandler},
* respectively.
* <p>The returned bean definition should <strong>not</strong> be used to create
* a bean instance but rather only for the purpose of having suitable bean
* definition metadata available in the {@link BeanFactory} &mdash; for example,
* for autowiring candidate resolution.
*/
private static RootBeanDefinition createPseudoBeanDefinition(OverrideMetadata metadata) {
RootBeanDefinition definition = new RootBeanDefinition(metadata.getBeanType().resolve());
definition.setTargetType(metadata.getBeanType());
definition.setQualifiedElement(metadata.getField());
private static RootBeanDefinition createPseudoBeanDefinition(BeanOverrideHandler handler) {
RootBeanDefinition definition = new RootBeanDefinition(handler.getBeanType().resolve());
definition.setTargetType(handler.getBeanType());
definition.setQualifiedElement(handler.getField());
return definition;
}

View File

@ -44,10 +44,10 @@ class BeanOverrideContextCustomizer implements ContextCustomizer {
"org.springframework.test.context.bean.override.internalWrapEarlyBeanPostProcessor";
private final Set<OverrideMetadata> metadata;
private final Set<BeanOverrideHandler> handlers;
BeanOverrideContextCustomizer(Set<OverrideMetadata> metadata) {
this.metadata = metadata;
BeanOverrideContextCustomizer(Set<BeanOverrideHandler> handlers) {
this.handlers = handlers;
}
@Override
@ -58,17 +58,17 @@ class BeanOverrideContextCustomizer implements ContextCustomizer {
// to register them as manual singleton instances. In addition, registration of
// the BeanOverrideBeanFactoryPostProcessor as a singleton is a requirement for
// AOT processing, since a bean definition cannot be generated for the
// Set<OverrideMetadata> argument that it accepts in its constructor.
// Set<BeanOverrideHandler> argument that it accepts in its constructor.
BeanOverrideRegistrar beanOverrideRegistrar = new BeanOverrideRegistrar(beanFactory);
beanFactory.registerSingleton(REGISTRAR_BEAN_NAME, beanOverrideRegistrar);
beanFactory.registerSingleton(INFRASTRUCTURE_BEAN_NAME,
new BeanOverrideBeanFactoryPostProcessor(this.metadata, beanOverrideRegistrar));
new BeanOverrideBeanFactoryPostProcessor(this.handlers, beanOverrideRegistrar));
beanFactory.registerSingleton(EARLY_INFRASTRUCTURE_BEAN_NAME,
new WrapEarlyBeanPostProcessor(beanOverrideRegistrar));
}
Set<OverrideMetadata> getMetadata() {
return this.metadata;
Set<BeanOverrideHandler> getBeanOverrideHandlers() {
return this.handlers;
}
@Override
@ -80,12 +80,12 @@ class BeanOverrideContextCustomizer implements ContextCustomizer {
return false;
}
BeanOverrideContextCustomizer that = (BeanOverrideContextCustomizer) other;
return this.metadata.equals(that.metadata);
return this.handlers.equals(that.handlers);
}
@Override
public int hashCode() {
return this.metadata.hashCode();
return this.handlers.hashCode();
}
}

View File

@ -31,6 +31,7 @@ import org.springframework.test.context.TestContextAnnotationUtils;
*
* @author Simon Baslé
* @author Stephane Nicoll
* @author Sam Brannen
* @since 6.2
* @see BeanOverride
*/
@ -41,19 +42,18 @@ class BeanOverrideContextCustomizerFactory implements ContextCustomizerFactory {
public BeanOverrideContextCustomizer createContextCustomizer(Class<?> testClass,
List<ContextConfigurationAttributes> configAttributes) {
Set<OverrideMetadata> metadata = new HashSet<>();
findOverrideMetadata(testClass, metadata);
if (metadata.isEmpty()) {
Set<BeanOverrideHandler> handlers = new HashSet<>();
findBeanOverrideHandler(testClass, handlers);
if (handlers.isEmpty()) {
return null;
}
return new BeanOverrideContextCustomizer(metadata);
return new BeanOverrideContextCustomizer(handlers);
}
private void findOverrideMetadata(Class<?> testClass, Set<OverrideMetadata> metadata) {
List<OverrideMetadata> overrideMetadata = OverrideMetadata.forTestClass(testClass);
metadata.addAll(overrideMetadata);
private void findBeanOverrideHandler(Class<?> testClass, Set<BeanOverrideHandler> handlers) {
handlers.addAll(BeanOverrideHandler.forTestClass(testClass));
if (TestContextAnnotationUtils.searchEnclosingClass(testClass)) {
findOverrideMetadata(testClass.getEnclosingClass(), metadata);
findBeanOverrideHandler(testClass.getEnclosingClass(), handlers);
}
}

View File

@ -41,16 +41,21 @@ import org.springframework.util.ReflectionUtils;
import static org.springframework.core.annotation.MergedAnnotations.SearchStrategy.DIRECT;
/**
* Metadata for Bean Override injection points, also responsible for creation of
* the overriding instance.
* Handler for Bean Override injection points that is responsible for creating
* the bean override instance for a given set of metadata and potentially for
* tracking the created instance.
*
* <p><strong>WARNING</strong>: implementations are used as a cache key and
* must implement proper {@code equals()} and {@code hashCode()} methods.
* <p><strong>WARNING</strong>: Implementations are used as a cache key and must
* implement proper {@code equals()} and {@code hashCode()} methods based on the
* unique set of metadata used to identify the bean to override. Overridden
* {@code equals()} and {@code hashCode()} methods should also delegate to the
* {@code super} implementations in this class in order to support the basic
* metadata used by all bean overrides.
*
* <p>Concrete implementations of {@code OverrideMetadata} can store state to use
* during override {@linkplain #createOverride(String, BeanDefinition, Object)
* instance creation} &mdash; for example, based on further processing of the
* annotation or the annotated field.
* <p>Concrete implementations of {@code BeanOverrideHandler} can store additional
* metadata to use during override {@linkplain #createOverrideInstance instance
* creation} &mdash; for example, based on further processing of the annotation
* or the annotated field.
*
* <p><strong>NOTE</strong>: Only <em>singleton</em> beans can be overridden.
* Any attempt to override a non-singleton bean will result in an exception.
@ -60,7 +65,7 @@ import static org.springframework.core.annotation.MergedAnnotations.SearchStrate
* @author Sam Brannen
* @since 6.2
*/
public abstract class OverrideMetadata {
public abstract class BeanOverrideHandler {
private final Field field;
@ -74,7 +79,7 @@ public abstract class OverrideMetadata {
private final BeanOverrideStrategy strategy;
protected OverrideMetadata(Field field, ResolvableType beanType, @Nullable String beanName,
protected BeanOverrideHandler(Field field, ResolvableType beanType, @Nullable String beanName,
BeanOverrideStrategy strategy) {
this.field = field;
@ -85,19 +90,19 @@ public abstract class OverrideMetadata {
}
/**
* Process the given {@code testClass} and build the corresponding list of
* {@code OverrideMetadata} derived from {@link BeanOverride @BeanOverride}
* fields in the test class and its type hierarchy.
* Process the given {@code testClass} and build the corresponding
* {@code BeanOverrideHandler} list derived from {@link BeanOverride @BeanOverride}
* fields in the test class, its type hierarchy, and its enclosing class hierarchy.
* @param testClass the test class to process
* @return a list of {@code OverrideMetadata}
* @return a list of bean override handlers
*/
public static List<OverrideMetadata> forTestClass(Class<?> testClass) {
List<OverrideMetadata> metadata = new LinkedList<>();
ReflectionUtils.doWithFields(testClass, field -> processField(field, testClass, metadata));
return metadata;
public static List<BeanOverrideHandler> forTestClass(Class<?> testClass) {
List<BeanOverrideHandler> handlers = new LinkedList<>();
ReflectionUtils.doWithFields(testClass, field -> processField(field, testClass, handlers));
return handlers;
}
private static void processField(Field field, Class<?> testClass, List<OverrideMetadata> metadataList) {
private static void processField(Field field, Class<?> testClass, List<BeanOverrideHandler> handlers) {
AtomicBoolean overrideAnnotationFound = new AtomicBoolean();
MergedAnnotations.from(field, DIRECT).stream(BeanOverride.class).forEach(mergedAnnotation -> {
MergedAnnotation<?> metaSource = mergedAnnotation.getMetaSource();
@ -109,8 +114,8 @@ public abstract class OverrideMetadata {
Assert.state(overrideAnnotationFound.compareAndSet(false, true),
() -> "Multiple @BeanOverride annotations found on field: " + field);
OverrideMetadata metadata = processor.createMetadata(composedAnnotation, testClass, field);
metadataList.add(metadata);
BeanOverrideHandler handler = processor.createHandler(composedAnnotation, testClass, field);
handlers.add(handler);
});
}
@ -139,7 +144,7 @@ public abstract class OverrideMetadata {
}
/**
* Get the {@link BeanOverrideStrategy} for this {@code OverrideMetadata},
* Get the {@link BeanOverrideStrategy} for this {@code BeanOverrideHandler},
* which influences how and when the bean override instance should be created.
*/
public final BeanOverrideStrategy getStrategy() {
@ -147,28 +152,31 @@ public abstract class OverrideMetadata {
}
/**
* Create a bean override instance for this {@code OverrideMetadata},
* optionally based on an existing {@link BeanDefinition} and/or an
* original instance, that is a singleton or an early wrapped instance.
* Create a bean override instance for an existing {@link BeanDefinition} or
* an existing singleton bean, based on the metadata in this
* {@code BeanOverrideHandler}.
* @param beanName the name of the bean being overridden
* @param existingBeanDefinition an existing bean definition for the supplied
* bean name, or {@code null} if irrelevant
* bean name, or {@code null} if not available or not relevant
* @param existingBeanInstance an existing instance for the supplied bean name
* for wrapping purposes, or {@code null} if irrelevant
* for wrapping purposes, or {@code null} if not available or not relevant
* @return the instance with which to override the bean
* @see #trackOverrideInstance(Object, SingletonBeanRegistry)
*/
protected abstract Object createOverride(String beanName, @Nullable BeanDefinition existingBeanDefinition,
@Nullable Object existingBeanInstance);
protected abstract Object createOverrideInstance(String beanName,
@Nullable BeanDefinition existingBeanDefinition, @Nullable Object existingBeanInstance);
/**
* Optionally track objects created by this {@code OverrideMetadata}.
* Track the supplied bean override instance that was created by this
* {@code BeanOverrideHandler}.
* <p>The default implementation does not track the supplied instance, but
* this can be overridden in subclasses as appropriate.
* @param override the bean override instance to track
* @param trackingBeanRegistry the registry in which trackers can
* optionally be registered
* @param trackingBeanRegistry a registry in which this handler can store
* tracking state in the form of a singleton bean
* @see #createOverrideInstance(String, BeanDefinition, Object)
*/
protected void track(Object override, SingletonBeanRegistry trackingBeanRegistry) {
protected void trackOverrideInstance(Object override, SingletonBeanRegistry trackingBeanRegistry) {
// NO-OP
}
@ -180,7 +188,7 @@ public abstract class OverrideMetadata {
if (other == null || other.getClass() != getClass()) {
return false;
}
OverrideMetadata that = (OverrideMetadata) other;
BeanOverrideHandler that = (BeanOverrideHandler) other;
if (!Objects.equals(this.beanType.getType(), that.beanType.getType()) ||
!Objects.equals(this.beanName, that.beanName) ||
!Objects.equals(this.strategy, that.strategy)) {

View File

@ -20,33 +20,33 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
/**
* Strategy interface for Bean Override processing, providing
* {@link OverrideMetadata} that drives how the target bean is overridden.
* Strategy interface for Bean Override processing, which creates a
* {@link BeanOverrideHandler} that drives how the target bean is overridden.
*
* <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
* {@code OverrideMetadata} is configured.
* may optionally provide annotation attributes that can be used to configure the
* {@code BeanOverrideHandler}.
*
* <p>Implementations are required to have a no-argument constructor and be
* stateless.
*
* @author Simon Baslé
* @author Stephane Nicoll
* @author Sam Brannen
* @since 6.2
*/
@FunctionalInterface
public interface BeanOverrideProcessor {
/**
* Create an {@link OverrideMetadata} instance for the given annotated field.
* Create a {@link BeanOverrideHandler} for the given annotated field.
* @param overrideAnnotation the composed annotation that declares the
* {@link BeanOverride @BeanOverride} annotation which registers this processor
* @param testClass the test class to process
* @param field the annotated field
* @return the {@link OverrideMetadata} instance that should handle the
* given field
* @return the {@code BeanOverrideHandler} that should handle the given field
*/
OverrideMetadata createMetadata(Annotation overrideAnnotation, Class<?> testClass, Field field);
BeanOverrideHandler createHandler(Annotation overrideAnnotation, Class<?> testClass, Field field);
}

View File

@ -28,7 +28,7 @@ import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* An internal class used to track {@link OverrideMetadata}-related state after
* An internal class used to track {@link BeanOverrideHandler}-related state after
* the bean factory has been processed and to provide field injection utilities
* for test execution listeners.
*
@ -38,9 +38,9 @@ import org.springframework.util.StringUtils;
*/
class BeanOverrideRegistrar {
private final Map<OverrideMetadata, String> beanNameRegistry = new HashMap<>();
private final Map<BeanOverrideHandler, String> beanNameRegistry = new HashMap<>();
private final Map<String, OverrideMetadata> earlyOverrideMetadata = new HashMap<>();
private final Map<String, BeanOverrideHandler> wrappingBeanOverrideHandlers = new HashMap<>();
private final ConfigurableBeanFactory beanFactory;
@ -51,40 +51,42 @@ class BeanOverrideRegistrar {
}
/**
* Check {@linkplain #markWrapEarly(OverrideMetadata, String) early override}
* records and use the {@link OverrideMetadata} to create an override
* instance based on the provided bean, if relevant.
* Register the provided {@link BeanOverrideHandler} and associate it with the
* given {@code beanName}.
*/
void registerBeanOverrideHandler(BeanOverrideHandler handler, String beanName) {
this.beanNameRegistry.put(handler, beanName);
}
/**
* Register the provided {@link BeanOverrideHandler} as a
* {@linkplain BeanOverrideStrategy#WRAP "wrapping"} handler and associate it
* with the given {@code beanName}, allowing for subsequent wrapping of the
* bean via {@link #wrapIfNecessary(Object, String)}.
*/
void registerWrappingBeanOverrideHandler(BeanOverrideHandler handler, String beanName) {
this.wrappingBeanOverrideHandlers.put(beanName, handler);
}
/**
* Check {@linkplain #registerWrappingBeanOverrideHandler(BeanOverrideHandler, String)
* wrapping handler} records and use the corresponding {@link BeanOverrideHandler}
* to create an override instance by wrapping the provided bean, if relevant.
*/
Object wrapIfNecessary(Object bean, String beanName) throws BeansException {
OverrideMetadata metadata = this.earlyOverrideMetadata.get(beanName);
if (metadata != null && metadata.getStrategy() == BeanOverrideStrategy.WRAP) {
bean = metadata.createOverride(beanName, null, bean);
metadata.track(bean, this.beanFactory);
BeanOverrideHandler handler = this.wrappingBeanOverrideHandlers.get(beanName);
if (handler != null && handler.getStrategy() == BeanOverrideStrategy.WRAP) {
bean = handler.createOverrideInstance(beanName, null, bean);
handler.trackOverrideInstance(bean, this.beanFactory);
}
return bean;
}
/**
* Register the provided {@link OverrideMetadata} and associate it with the
* supplied {@code beanName}.
*/
void registerNameForMetadata(OverrideMetadata metadata, String beanName) {
this.beanNameRegistry.put(metadata, beanName);
}
/**
* Mark the provided {@link OverrideMetadata} and {@code beanName} as "wrap
* early", allowing for later bean override using {@link #wrapIfNecessary(Object, String)}.
*/
void markWrapEarly(OverrideMetadata metadata, String beanName) {
this.earlyOverrideMetadata.put(beanName, metadata);
}
void inject(Object target, OverrideMetadata overrideMetadata) {
String beanName = this.beanNameRegistry.get(overrideMetadata);
void inject(Object target, BeanOverrideHandler handler) {
String beanName = this.beanNameRegistry.get(handler);
Assert.state(StringUtils.hasLength(beanName),
() -> "No bean found for OverrideMetadata: " + overrideMetadata);
inject(overrideMetadata.getField(), target, beanName);
() -> "No bean found for BeanOverrideHandler: " + handler);
inject(handler.getField(), target, beanName);
}
private void inject(Field field, Object target, String beanName) {

View File

@ -77,14 +77,14 @@ public class BeanOverrideTestExecutionListener extends AbstractTestExecutionList
* a corresponding bean override instance.
*/
private static void injectFields(TestContext testContext) {
List<OverrideMetadata> overrideMetadataList = OverrideMetadata.forTestClass(testContext.getTestClass());
if (!overrideMetadataList.isEmpty()) {
List<BeanOverrideHandler> handlerList = BeanOverrideHandler.forTestClass(testContext.getTestClass());
if (!handlerList.isEmpty()) {
Object testInstance = testContext.getTestInstance();
BeanOverrideRegistrar registrar = testContext.getApplicationContext()
.getBean(BeanOverrideContextCustomizer.REGISTRAR_BEAN_NAME, BeanOverrideRegistrar.class);
for (OverrideMetadata overrideMetadata : overrideMetadataList) {
registrar.inject(testInstance, overrideMetadata);
for (BeanOverrideHandler handler : handlerList) {
registrar.inject(testInstance, handler);
}
}
}

View File

@ -24,41 +24,41 @@ import java.util.Objects;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.test.context.bean.override.BeanOverrideHandler;
import org.springframework.test.context.bean.override.BeanOverrideStrategy;
import org.springframework.test.context.bean.override.OverrideMetadata;
import org.springframework.util.ReflectionUtils;
/**
* {@link OverrideMetadata} implementation for {@link TestBean}.
* {@link BeanOverrideHandler} implementation for {@link TestBean}.
*
* @author Simon Baslé
* @author Stephane Nicoll
* @author Sam Brannen
* @since 6.2
*/
final class TestBeanOverrideMetadata extends OverrideMetadata {
final class TestBeanBeanOverrideHandler extends BeanOverrideHandler {
private final Method overrideMethod;
private final Method factoryMethod;
TestBeanOverrideMetadata(Field field, ResolvableType beanType, @Nullable String beanName,
BeanOverrideStrategy strategy, Method overrideMethod) {
TestBeanBeanOverrideHandler(Field field, ResolvableType beanType, @Nullable String beanName,
BeanOverrideStrategy strategy, Method factoryMethod) {
super(field, beanType, beanName, strategy);
this.overrideMethod = overrideMethod;
this.factoryMethod = factoryMethod;
}
@Override
protected Object createOverride(String beanName, @Nullable BeanDefinition existingBeanDefinition,
protected Object createOverrideInstance(String beanName, @Nullable BeanDefinition existingBeanDefinition,
@Nullable Object existingBeanInstance) {
try {
ReflectionUtils.makeAccessible(this.overrideMethod);
return this.overrideMethod.invoke(null);
ReflectionUtils.makeAccessible(this.factoryMethod);
return this.factoryMethod.invoke(null);
}
catch (IllegalAccessException | InvocationTargetException ex) {
throw new IllegalStateException("Failed to invoke bean overriding method " + this.overrideMethod.getName() +
throw new IllegalStateException("Failed to invoke bean overriding method " + this.factoryMethod.getName() +
"; a static method with no formal parameters is expected", ex);
}
}
@ -74,13 +74,13 @@ final class TestBeanOverrideMetadata extends OverrideMetadata {
if (!super.equals(other)) {
return false;
}
TestBeanOverrideMetadata that = (TestBeanOverrideMetadata) other;
return Objects.equals(this.overrideMethod, that.overrideMethod);
TestBeanBeanOverrideHandler that = (TestBeanBeanOverrideHandler) other;
return Objects.equals(this.factoryMethod, that.factoryMethod);
}
@Override
public int hashCode() {
return this.overrideMethod.hashCode() * 29 + super.hashCode();
return this.factoryMethod.hashCode() * 29 + super.hashCode();
}
}

View File

@ -42,9 +42,9 @@ import static org.springframework.test.context.bean.override.BeanOverrideStrateg
/**
* {@link BeanOverrideProcessor} implementation for {@link TestBean @TestBean}
* support, which creates metadata for annotated fields in a given class and
* ensures that a corresponding static factory method exists, according to the
* {@linkplain TestBean documented conventions}.
* support, which creates a {@link TestBeanBeanOverrideHandler} for annotated
* fields in a given class and ensures that a corresponding static factory method
* exists, according to the {@linkplain TestBean documented conventions}.
*
* @author Simon Baslé
* @author Sam Brannen
@ -54,7 +54,7 @@ import static org.springframework.test.context.bean.override.BeanOverrideStrateg
class TestBeanOverrideProcessor implements BeanOverrideProcessor {
@Override
public TestBeanOverrideMetadata createMetadata(Annotation overrideAnnotation, Class<?> testClass, Field field) {
public TestBeanBeanOverrideHandler createHandler(Annotation overrideAnnotation, Class<?> testClass, Field field) {
if (!(overrideAnnotation instanceof TestBean testBean)) {
throw new IllegalStateException("Invalid annotation passed to %s: expected @TestBean on field %s.%s"
.formatted(getClass().getSimpleName(), field.getDeclaringClass().getName(), field.getName()));
@ -64,10 +64,10 @@ class TestBeanOverrideProcessor implements BeanOverrideProcessor {
String methodName = testBean.methodName();
BeanOverrideStrategy strategy = (testBean.enforceOverride() ? REPLACE : REPLACE_OR_CREATE);
Method overrideMethod;
Method factoryMethod;
if (!methodName.isBlank()) {
// If the user specified an explicit method name, search for that.
overrideMethod = findTestBeanFactoryMethod(testClass, field.getType(), methodName);
factoryMethod = findTestBeanFactoryMethod(testClass, field.getType(), methodName);
}
else {
// Otherwise, search for candidate factory methods whose names match either
@ -78,11 +78,11 @@ class TestBeanOverrideProcessor implements BeanOverrideProcessor {
if (beanName != null) {
candidateMethodNames.add(beanName);
}
overrideMethod = findTestBeanFactoryMethod(testClass, field.getType(), candidateMethodNames);
factoryMethod = findTestBeanFactoryMethod(testClass, field.getType(), candidateMethodNames);
}
return new TestBeanOverrideMetadata(
field, ResolvableType.forField(field, testClass), beanName, strategy, overrideMethod);
return new TestBeanBeanOverrideHandler(
field, ResolvableType.forField(field, testClass), beanName, strategy, factoryMethod);
}
/**

View File

@ -23,25 +23,25 @@ import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.core.ResolvableType;
import org.springframework.core.style.ToStringCreator;
import org.springframework.lang.Nullable;
import org.springframework.test.context.bean.override.BeanOverrideHandler;
import org.springframework.test.context.bean.override.BeanOverrideStrategy;
import org.springframework.test.context.bean.override.OverrideMetadata;
/**
* Abstract base {@link OverrideMetadata} implementation for Mockito.
* Abstract base {@link BeanOverrideHandler} implementation for Mockito.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @author Sam Brannen
* @since 6.2
*/
abstract class AbstractMockitoOverrideMetadata extends OverrideMetadata {
abstract class AbstractMockitoBeanOverrideHandler extends BeanOverrideHandler {
private final MockReset reset;
private final boolean proxyTargetAware;
protected AbstractMockitoOverrideMetadata(Field field, ResolvableType beanType, @Nullable String beanName,
protected AbstractMockitoBeanOverrideHandler(Field field, ResolvableType beanType, @Nullable String beanName,
BeanOverrideStrategy strategy, @Nullable MockReset reset, boolean proxyTargetAware) {
super(field, beanType, beanName, strategy);
@ -67,7 +67,7 @@ abstract class AbstractMockitoOverrideMetadata extends OverrideMetadata {
}
@Override
protected void track(Object mock, SingletonBeanRegistry trackingBeanRegistry) {
protected void trackOverrideInstance(Object mock, SingletonBeanRegistry trackingBeanRegistry) {
getMockitoBeans(trackingBeanRegistry).add(mock);
}
@ -89,7 +89,7 @@ abstract class AbstractMockitoOverrideMetadata extends OverrideMetadata {
if (other == this) {
return true;
}
return (other instanceof AbstractMockitoOverrideMetadata that && super.equals(that) &&
return (other instanceof AbstractMockitoBeanOverrideHandler that && super.equals(that) &&
(this.reset == that.reset) && (this.proxyTargetAware == that.proxyTargetAware));
}

View File

@ -31,8 +31,8 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.core.ResolvableType;
import org.springframework.core.style.ToStringCreator;
import org.springframework.lang.Nullable;
import org.springframework.test.context.bean.override.BeanOverrideHandler;
import org.springframework.test.context.bean.override.BeanOverrideStrategy;
import org.springframework.test.context.bean.override.OverrideMetadata;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@ -41,14 +41,14 @@ import static org.springframework.test.context.bean.override.BeanOverrideStrateg
import static org.springframework.test.context.bean.override.BeanOverrideStrategy.REPLACE_OR_CREATE;
/**
* {@link OverrideMetadata} implementation for Mockito {@code mock} support.
* {@link BeanOverrideHandler} implementation for Mockito {@code mock} support.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @author Sam Brannen
* @since 6.2
*/
class MockitoBeanOverrideMetadata extends AbstractMockitoOverrideMetadata {
class MockitoBeanOverrideHandler extends AbstractMockitoBeanOverrideHandler {
private final Set<Class<?>> extraInterfaces;
@ -57,13 +57,13 @@ class MockitoBeanOverrideMetadata extends AbstractMockitoOverrideMetadata {
private final boolean serializable;
MockitoBeanOverrideMetadata(Field field, ResolvableType typeToMock, MockitoBean mockitoBean) {
MockitoBeanOverrideHandler(Field field, ResolvableType typeToMock, MockitoBean mockitoBean) {
this(field, typeToMock, (!mockitoBean.name().isBlank() ? mockitoBean.name() : null),
(mockitoBean.enforceOverride() ? REPLACE : REPLACE_OR_CREATE),
mockitoBean.reset(), mockitoBean.extraInterfaces(), mockitoBean.answers(), mockitoBean.serializable());
}
private MockitoBeanOverrideMetadata(Field field, ResolvableType typeToMock, @Nullable String beanName,
private MockitoBeanOverrideHandler(Field field, ResolvableType typeToMock, @Nullable String beanName,
BeanOverrideStrategy strategy, MockReset reset, Class<?>[] extraInterfaces, @Nullable Answers answers,
boolean serializable) {
@ -109,7 +109,7 @@ class MockitoBeanOverrideMetadata extends AbstractMockitoOverrideMetadata {
}
@Override
protected Object createOverride(String beanName, @Nullable BeanDefinition existingBeanDefinition, @Nullable Object existingBeanInstance) {
protected Object createOverrideInstance(String beanName, @Nullable BeanDefinition existingBeanDefinition, @Nullable Object existingBeanInstance) {
return createMock(beanName);
}
@ -138,7 +138,7 @@ class MockitoBeanOverrideMetadata extends AbstractMockitoOverrideMetadata {
if (other == null || other.getClass() != getClass()) {
return false;
}
return (other instanceof MockitoBeanOverrideMetadata that && super.equals(that) &&
return (other instanceof MockitoBeanOverrideHandler that && super.equals(that) &&
(this.serializable == that.serializable) && (this.answers == that.answers) &&
Objects.equals(this.extraInterfaces, that.extraInterfaces));
}

View File

@ -34,12 +34,12 @@ import org.springframework.test.context.bean.override.BeanOverrideProcessor;
class MockitoBeanOverrideProcessor implements BeanOverrideProcessor {
@Override
public AbstractMockitoOverrideMetadata createMetadata(Annotation overrideAnnotation, Class<?> testClass, Field field) {
public AbstractMockitoBeanOverrideHandler createHandler(Annotation overrideAnnotation, Class<?> testClass, Field field) {
if (overrideAnnotation instanceof MockitoBean mockBean) {
return new MockitoBeanOverrideMetadata(field, ResolvableType.forField(field, testClass), mockBean);
return new MockitoBeanOverrideHandler(field, ResolvableType.forField(field, testClass), mockBean);
}
else if (overrideAnnotation instanceof MockitoSpyBean spyBean) {
return new MockitoSpyBeanOverrideMetadata(field, ResolvableType.forField(field, testClass), spyBean);
return new MockitoSpyBeanOverrideHandler(field, ResolvableType.forField(field, testClass), spyBean);
}
throw new IllegalStateException("""
Invalid annotation passed to MockitoBeanOverrideProcessor: \

View File

@ -28,28 +28,28 @@ import org.mockito.listeners.VerificationStartedListener;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.test.context.bean.override.BeanOverrideHandler;
import org.springframework.test.context.bean.override.BeanOverrideStrategy;
import org.springframework.test.context.bean.override.OverrideMetadata;
import org.springframework.test.util.AopTestUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* {@link OverrideMetadata} implementation for Mockito {@code spy} support.
* {@link BeanOverrideHandler} implementation for Mockito {@code spy} support.
*
* @author Phillip Webb
* @author Simon Baslé
* @author Stephane Nicoll
* @since 6.2
*/
class MockitoSpyBeanOverrideMetadata extends AbstractMockitoOverrideMetadata {
class MockitoSpyBeanOverrideHandler extends AbstractMockitoBeanOverrideHandler {
MockitoSpyBeanOverrideMetadata(Field field, ResolvableType typeToSpy, MockitoSpyBean spyAnnotation) {
MockitoSpyBeanOverrideHandler(Field field, ResolvableType typeToSpy, MockitoSpyBean spyAnnotation) {
this(field, typeToSpy, (StringUtils.hasText(spyAnnotation.name()) ? spyAnnotation.name() : null),
spyAnnotation.reset(), spyAnnotation.proxyTargetAware());
}
MockitoSpyBeanOverrideMetadata(Field field, ResolvableType typeToSpy, @Nullable String beanName,
MockitoSpyBeanOverrideHandler(Field field, ResolvableType typeToSpy, @Nullable String beanName,
MockReset reset, boolean proxyTargetAware) {
super(field, typeToSpy, beanName, BeanOverrideStrategy.WRAP, reset, proxyTargetAware);
@ -58,7 +58,7 @@ class MockitoSpyBeanOverrideMetadata extends AbstractMockitoOverrideMetadata {
@Override
protected Object createOverride(String beanName, @Nullable BeanDefinition existingBeanDefinition,
protected Object createOverrideInstance(String beanName, @Nullable BeanDefinition existingBeanDefinition,
@Nullable Object existingBeanInstance) {
Assert.notNull(existingBeanInstance,

View File

@ -354,8 +354,8 @@ class BeanOverrideBeanFactoryPostProcessorTests {
private AnnotationConfigApplicationContext createContext(Class<?> testClass) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
Set<OverrideMetadata> metadata = new LinkedHashSet<>(OverrideMetadata.forTestClass(testClass));
new BeanOverrideContextCustomizer(metadata).customizeContext(context, mock(MergedContextConfiguration.class));
Set<BeanOverrideHandler> handlers = new LinkedHashSet<>(BeanOverrideHandler.forTestClass(testClass));
new BeanOverrideContextCustomizer(handlers).customizeContext(context, mock(MergedContextConfiguration.class));
return context;
}

View File

@ -23,9 +23,7 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.lang.Nullable;
import org.springframework.test.context.bean.override.BeanOverrideContextCustomizerFactoryTests.Test2.Green;
import org.springframework.test.context.bean.override.BeanOverrideContextCustomizerFactoryTests.Test2.Orange;
import org.springframework.test.context.bean.override.DummyBean.DummyBeanOverrideProcessor.DummyOverrideMetadata;
import org.springframework.test.context.bean.override.DummyBean.DummyBeanOverrideProcessor.DummyBeanOverrideHandler;
import static org.assertj.core.api.Assertions.assertThat;
@ -47,37 +45,37 @@ class BeanOverrideContextCustomizerFactoryTests {
void createContextCustomizerWhenTestHasSingleBeanOverride() {
BeanOverrideContextCustomizer customizer = createContextCustomizer(Test1.class);
assertThat(customizer).isNotNull();
assertThat(customizer.getMetadata()).singleElement().satisfies(dummyMetadata(null, String.class));
assertThat(customizer.getBeanOverrideHandlers()).singleElement().satisfies(dummyHandler(null, String.class));
}
@Test
void createContextCustomizerWhenNestedTestHasSingleBeanOverrideInParent() {
BeanOverrideContextCustomizer customizer = createContextCustomizer(Orange.class);
BeanOverrideContextCustomizer customizer = createContextCustomizer(Test2.Orange.class);
assertThat(customizer).isNotNull();
assertThat(customizer.getMetadata()).singleElement().satisfies(dummyMetadata(null, String.class));
assertThat(customizer.getBeanOverrideHandlers()).singleElement().satisfies(dummyHandler(null, String.class));
}
@Test
void createContextCustomizerWhenNestedTestHasBeanOverrideAsWellAsTheParent() {
BeanOverrideContextCustomizer customizer = createContextCustomizer(Green.class);
BeanOverrideContextCustomizer customizer = createContextCustomizer(Test2.Green.class);
assertThat(customizer).isNotNull();
assertThat(customizer.getMetadata())
.anySatisfy(dummyMetadata(null, String.class))
.anySatisfy(dummyMetadata("counterBean", Integer.class))
assertThat(customizer.getBeanOverrideHandlers())
.anySatisfy(dummyHandler(null, String.class))
.anySatisfy(dummyHandler("counterBean", Integer.class))
.hasSize(2);
}
private Consumer<OverrideMetadata> dummyMetadata(@Nullable String beanName, Class<?> beanType) {
return dummyMetadata(beanName, beanType, BeanOverrideStrategy.REPLACE);
private Consumer<BeanOverrideHandler> dummyHandler(@Nullable String beanName, Class<?> beanType) {
return dummyHandler(beanName, beanType, BeanOverrideStrategy.REPLACE);
}
private Consumer<OverrideMetadata> dummyMetadata(@Nullable String beanName, Class<?> beanType, BeanOverrideStrategy strategy) {
return metadata -> {
assertThat(metadata).isExactlyInstanceOf(DummyOverrideMetadata.class);
assertThat(metadata.getBeanName()).isEqualTo(beanName);
assertThat(metadata.getBeanType().toClass()).isEqualTo(beanType);
assertThat(metadata.getStrategy()).isEqualTo(strategy);
private Consumer<BeanOverrideHandler> dummyHandler(@Nullable String beanName, Class<?> beanType, BeanOverrideStrategy strategy) {
return handler -> {
assertThat(handler).isExactlyInstanceOf(DummyBeanOverrideHandler.class);
assertThat(handler.getBeanName()).isEqualTo(beanName);
assertThat(handler.getBeanType().toClass()).isEqualTo(beanType);
assertThat(handler.getStrategy()).isEqualTo(strategy);
};
}
@ -100,7 +98,6 @@ class BeanOverrideContextCustomizerFactoryTests {
@Nested
class Orange {
}
@Nested

View File

@ -38,8 +38,8 @@ class BeanOverrideContextCustomizerTests {
@Test
void customizerIsEqualWithIdenticalMetadata() {
BeanOverrideContextCustomizer customizer = createCustomizer(new DummyOverrideMetadata("key"));
BeanOverrideContextCustomizer customizer2 = createCustomizer(new DummyOverrideMetadata("key"));
BeanOverrideContextCustomizer customizer = createCustomizer(new DummyBeanOverrideHandler("key"));
BeanOverrideContextCustomizer customizer2 = createCustomizer(new DummyBeanOverrideHandler("key"));
assertThat(customizer).isEqualTo(customizer2);
assertThat(customizer).hasSameHashCodeAs(customizer2);
}
@ -47,37 +47,37 @@ class BeanOverrideContextCustomizerTests {
@Test
void customizerIsEqualWithIdenticalMetadataInDifferentOrder() {
BeanOverrideContextCustomizer customizer = createCustomizer(
new DummyOverrideMetadata("key1"), new DummyOverrideMetadata("key2"));
new DummyBeanOverrideHandler("key1"), new DummyBeanOverrideHandler("key2"));
BeanOverrideContextCustomizer customizer2 = createCustomizer(
new DummyOverrideMetadata("key2"), new DummyOverrideMetadata("key1"));
new DummyBeanOverrideHandler("key2"), new DummyBeanOverrideHandler("key1"));
assertThat(customizer).isEqualTo(customizer2);
assertThat(customizer).hasSameHashCodeAs(customizer2);
}
@Test
void customizerIsNotEqualWithDifferentMetadata() {
BeanOverrideContextCustomizer customizer = createCustomizer(new DummyOverrideMetadata("key"));
BeanOverrideContextCustomizer customizer = createCustomizer(new DummyBeanOverrideHandler("key"));
BeanOverrideContextCustomizer customizer2 = createCustomizer(
new DummyOverrideMetadata("key"), new DummyOverrideMetadata("another"));
new DummyBeanOverrideHandler("key"), new DummyBeanOverrideHandler("another"));
assertThat(customizer).isNotEqualTo(customizer2);
}
private BeanOverrideContextCustomizer createCustomizer(OverrideMetadata... metadata) {
return new BeanOverrideContextCustomizer(new LinkedHashSet<>(Arrays.asList(metadata)));
private BeanOverrideContextCustomizer createCustomizer(BeanOverrideHandler... handlers) {
return new BeanOverrideContextCustomizer(new LinkedHashSet<>(Arrays.asList(handlers)));
}
private static class DummyOverrideMetadata extends OverrideMetadata {
private static class DummyBeanOverrideHandler extends BeanOverrideHandler {
private final String key;
public DummyOverrideMetadata(String key) {
super(ReflectionUtils.findField(DummyOverrideMetadata.class, "key"),
public DummyBeanOverrideHandler(String key) {
super(ReflectionUtils.findField(DummyBeanOverrideHandler.class, "key"),
ResolvableType.forClass(Object.class), null, BeanOverrideStrategy.REPLACE);
this.key = key;
}
@Override
protected Object createOverride(String beanName, BeanDefinition existingBeanDefinition,
protected Object createOverrideInstance(String beanName, BeanDefinition existingBeanDefinition,
Object existingBeanInstance) {
return existingBeanInstance;
}
@ -90,7 +90,7 @@ class BeanOverrideContextCustomizerTests {
if (o == null || getClass() != o.getClass()) {
return false;
}
DummyOverrideMetadata that = (DummyOverrideMetadata) o;
DummyBeanOverrideHandler that = (DummyBeanOverrideHandler) o;
return Objects.equals(this.key, that.key);
}

View File

@ -0,0 +1,286 @@
/*
* Copyright 2002-2024 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.bean.override;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.lang.Nullable;
import org.springframework.test.context.bean.override.DummyBean.DummyBeanOverrideProcessor.DummyBeanOverrideHandler;
import org.springframework.test.context.bean.override.example.CustomQualifier;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.assertj.core.api.SoftAssertions.assertSoftly;
/**
* Tests for {@link BeanOverrideHandler}.
*
* @author Simon Baslé
* @author Stephane Nicoll
* @author Sam Brannen
* @since 6.2
*/
class BeanOverrideHandlerTests {
@Test
void forTestClassWithSingleField() {
List<BeanOverrideHandler> handlers = BeanOverrideHandler.forTestClass(SingleAnnotation.class);
assertThat(handlers).singleElement().satisfies(hasBeanOverrideHandler(
field(SingleAnnotation.class, "message"), String.class, null));
}
@Test
void forTestClassWithMultipleFields() {
List<BeanOverrideHandler> handlers = BeanOverrideHandler.forTestClass(MultipleAnnotations.class);
assertThat(handlers).hasSize(2)
.anySatisfy(hasBeanOverrideHandler(
field(MultipleAnnotations.class, "message"), String.class, null))
.anySatisfy(hasBeanOverrideHandler(
field(MultipleAnnotations.class, "counter"), Integer.class, null));
}
@Test
void forTestClassWithMultipleFieldsWithIdenticalMetadata() {
List<BeanOverrideHandler> handlers = BeanOverrideHandler.forTestClass(MultipleAnnotationsDuplicate.class);
assertThat(handlers).hasSize(2)
.anySatisfy(hasBeanOverrideHandler(
field(MultipleAnnotationsDuplicate.class, "message1"), String.class, "messageBean"))
.anySatisfy(hasBeanOverrideHandler(
field(MultipleAnnotationsDuplicate.class, "message2"), String.class, "messageBean"));
assertThat(new HashSet<>(handlers)).hasSize(1);
}
@Test
void forTestClassWithCompetingBeanOverrideAnnotationsOnSameField() {
Field faultyField = field(MultipleAnnotationsOnSameField.class, "message");
assertThatIllegalStateException()
.isThrownBy(() -> BeanOverrideHandler.forTestClass(MultipleAnnotationsOnSameField.class))
.withMessageStartingWith("Multiple @BeanOverride annotations found")
.withMessageContaining(faultyField.toString());
}
@Test
void getBeanNameIsNullByDefault() {
BeanOverrideHandler handler = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"));
assertThat(handler.getBeanName()).isNull();
}
@Test
void isEqualToWithSameInstance() {
BeanOverrideHandler handler = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"));
assertThat(handler).isEqualTo(handler);
assertThat(handler).hasSameHashCodeAs(handler);
}
@Test
void isEqualToWithSameMetadata() {
BeanOverrideHandler handler1 = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"));
BeanOverrideHandler handler2 = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"));
assertThat(handler1).isEqualTo(handler2);
assertThat(handler1).hasSameHashCodeAs(handler2);
}
@Test
void isEqualToWithSameMetadataAndBeanNames() {
BeanOverrideHandler handler1 = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"), "testBean");
BeanOverrideHandler handler2 = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"), "testBean");
assertThat(handler1).isEqualTo(handler2);
assertThat(handler1).hasSameHashCodeAs(handler2);
}
@Test
void isNotEqualToWithSameMetadataAndDifferentBeaName() {
BeanOverrideHandler handler1 = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"), "testBean");
BeanOverrideHandler handler2 = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"), "testBean2");
assertThat(handler1).isNotEqualTo(handler2);
}
@Test
void isEqualToWithSameMetadataButDifferentFields() {
BeanOverrideHandler handler1 = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"));
BeanOverrideHandler handler2 = createBeanOverrideHandler(field(ConfigB.class, "noQualifier"));
assertThat(handler1).isEqualTo(handler2);
assertThat(handler1).hasSameHashCodeAs(handler2);
}
@Test
void isEqualToWithByNameLookupAndDifferentFieldNames() {
BeanOverrideHandler handler1 = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"), "beanToOverride");
BeanOverrideHandler handler2 = createBeanOverrideHandler(field(ConfigB.class, "example"), "beanToOverride");
assertThat(handler1).isEqualTo(handler2);
assertThat(handler1).hasSameHashCodeAs(handler2);
}
@Test
void isEqualToWithSameMetadataAndSameQualifierValues() {
BeanOverrideHandler handler1 = createBeanOverrideHandler(field(ConfigA.class, "directQualifier"));
BeanOverrideHandler handler2 = createBeanOverrideHandler(field(ConfigB.class, "directQualifier"));
assertThat(handler1).isEqualTo(handler2);
assertThat(handler1).hasSameHashCodeAs(handler2);
}
@Test
void isEqualToWithSameMetadataAndSameQualifierValuesButWithAnnotationsDeclaredInDifferentOrder() {
Field field1 = field(ConfigA.class, "qualifiedDummyBean");
Field field2 = field(ConfigB.class, "qualifiedDummyBean");
// Prerequisite
assertThat(Arrays.equals(field1.getAnnotations(), field2.getAnnotations())).isFalse();
BeanOverrideHandler handler1 = createBeanOverrideHandler(field1);
BeanOverrideHandler handler2 = createBeanOverrideHandler(field2);
assertThat(handler1).isEqualTo(handler2);
assertThat(handler1).hasSameHashCodeAs(handler2);
}
@Test
void isNotEqualToWithSameMetadataAndDifferentQualifierValues() {
BeanOverrideHandler handler1 = createBeanOverrideHandler(field(ConfigA.class, "directQualifier"));
BeanOverrideHandler handler2 = createBeanOverrideHandler(field(ConfigA.class, "differentDirectQualifier"));
assertThat(handler1).isNotEqualTo(handler2);
}
@Test
void isNotEqualToWithSameMetadataAndDifferentQualifiers() {
BeanOverrideHandler handler1 = createBeanOverrideHandler(field(ConfigA.class, "directQualifier"));
BeanOverrideHandler handler2 = createBeanOverrideHandler(field(ConfigA.class, "customQualifier"));
assertThat(handler1).isNotEqualTo(handler2);
}
@Test
void isNotEqualToWithByTypeLookupAndDifferentFieldNames() {
BeanOverrideHandler handler1 = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"));
BeanOverrideHandler handler2 = createBeanOverrideHandler(field(ConfigB.class, "example"));
assertThat(handler1).isNotEqualTo(handler2);
}
private static BeanOverrideHandler createBeanOverrideHandler(Field field) {
return createBeanOverrideHandler(field, null);
}
private static BeanOverrideHandler createBeanOverrideHandler(Field field, @Nullable String name) {
return new DummyBeanOverrideHandler(field, field.getType(), name, BeanOverrideStrategy.REPLACE);
}
private static Field field(Class<?> target, String fieldName) {
Field field = ReflectionUtils.findField(target, fieldName);
assertThat(field).isNotNull();
return field;
}
private static Consumer<BeanOverrideHandler> hasBeanOverrideHandler(Field field, Class<?> beanType, @Nullable String beanName) {
return hasBeanOverrideHandler(field, beanType, BeanOverrideStrategy.REPLACE, beanName);
}
private static Consumer<BeanOverrideHandler> hasBeanOverrideHandler(Field field, Class<?> beanType, BeanOverrideStrategy strategy,
@Nullable String beanName) {
return handler -> assertSoftly(softly -> {
softly.assertThat(handler.getField()).as("field").isEqualTo(field);
softly.assertThat(handler.getBeanType().toClass()).as("type").isEqualTo(beanType);
softly.assertThat(handler.getBeanName()).as("name").isEqualTo(beanName);
softly.assertThat(handler.getStrategy()).as("strategy").isEqualTo(strategy);
});
}
static class SingleAnnotation {
@DummyBean
String message;
}
static class MultipleAnnotations {
@DummyBean
String message;
@DummyBean
Integer counter;
}
static class MultipleAnnotationsDuplicate {
@DummyBean(beanName = "messageBean")
String message1;
@DummyBean(beanName = "messageBean")
String message2;
}
static class MultipleAnnotationsOnSameField {
@MetaDummyBean()
@DummyBean
String message;
static String foo() {
return "foo";
}
}
static class ConfigA {
ExampleService noQualifier;
@Qualifier("test")
ExampleService directQualifier;
@Qualifier("different")
ExampleService differentDirectQualifier;
@CustomQualifier
ExampleService customQualifier;
@DummyBean
@Qualifier("test")
ExampleService qualifiedDummyBean;
}
static class ConfigB {
ExampleService noQualifier;
ExampleService example;
@Qualifier("test")
ExampleService directQualifier;
@Qualifier("test")
@DummyBean
ExampleService qualifiedDummyBean;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@DummyBean
@interface MetaDummyBean {}
}

View File

@ -50,25 +50,25 @@ import org.springframework.util.StringUtils;
class DummyBeanOverrideProcessor implements BeanOverrideProcessor {
@Override
public OverrideMetadata createMetadata(Annotation annotation, Class<?> testClass, Field field) {
public BeanOverrideHandler createHandler(Annotation annotation, Class<?> testClass, Field field) {
DummyBean dummyBean = (DummyBean) annotation;
String beanName = (StringUtils.hasText(dummyBean.beanName()) ? dummyBean.beanName() : null);
return new DummyBeanOverrideProcessor.DummyOverrideMetadata(field, field.getType(), beanName,
return new DummyBeanOverrideProcessor.DummyBeanOverrideHandler(field, field.getType(), beanName,
dummyBean.strategy());
}
// Bare bone, "dummy", implementation that should not override anything
// else than createOverride.
static class DummyOverrideMetadata extends OverrideMetadata {
// other than createOverrideInstance().
static class DummyBeanOverrideHandler extends BeanOverrideHandler {
DummyOverrideMetadata(Field field, Class<?> typeToOverride, @Nullable String beanName,
DummyBeanOverrideHandler(Field field, Class<?> typeToOverride, @Nullable String beanName,
BeanOverrideStrategy strategy) {
super(field, ResolvableType.forClass(typeToOverride), beanName, strategy);
}
@Override
protected Object createOverride(String beanName, @Nullable BeanDefinition existingBeanDefinition,
protected Object createOverrideInstance(String beanName, @Nullable BeanDefinition existingBeanDefinition,
@Nullable Object existingBeanInstance) {
Class<?> beanType = getField().getType();

View File

@ -1,285 +0,0 @@
/*
* Copyright 2002-2024 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.bean.override;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.lang.Nullable;
import org.springframework.test.context.bean.override.DummyBean.DummyBeanOverrideProcessor.DummyOverrideMetadata;
import org.springframework.test.context.bean.override.example.CustomQualifier;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Tests for {@link OverrideMetadata}.
*
* @author Simon Baslé
* @author Stephane Nicoll
* @author Sam Brannen
* @since 6.2
*/
class OverrideMetadataTests {
@Test
void forTestClassWithSingleField() {
List<OverrideMetadata> overrideMetadata = OverrideMetadata.forTestClass(SingleAnnotation.class);
assertThat(overrideMetadata).singleElement().satisfies(hasOverrideMetadata(
field(SingleAnnotation.class, "message"), String.class, null));
}
@Test
void forTestClassWithMultipleFields() {
List<OverrideMetadata> overrideMetadata = OverrideMetadata.forTestClass(MultipleAnnotations.class);
assertThat(overrideMetadata).hasSize(2)
.anySatisfy(hasOverrideMetadata(
field(MultipleAnnotations.class, "message"), String.class, null))
.anySatisfy(hasOverrideMetadata(
field(MultipleAnnotations.class, "counter"), Integer.class, null));
}
@Test
void forTestClassWithMultipleFieldsWithIdenticalMetadata() {
List<OverrideMetadata> overrideMetadata = OverrideMetadata.forTestClass(MultipleAnnotationsDuplicate.class);
assertThat(overrideMetadata).hasSize(2)
.anySatisfy(hasOverrideMetadata(
field(MultipleAnnotationsDuplicate.class, "message1"), String.class, "messageBean"))
.anySatisfy(hasOverrideMetadata(
field(MultipleAnnotationsDuplicate.class, "message2"), String.class, "messageBean"));
assertThat(new HashSet<>(overrideMetadata)).hasSize(1);
}
@Test
void forTestClassWithCompetingBeanOverrideAnnotationsOnSameField() {
Field faultyField = field(MultipleAnnotationsOnSameField.class, "message");
assertThatIllegalStateException()
.isThrownBy(() -> OverrideMetadata.forTestClass(MultipleAnnotationsOnSameField.class))
.withMessageStartingWith("Multiple @BeanOverride annotations found")
.withMessageContaining(faultyField.toString());
}
@Test
void getBeanNameIsNullByDefault() {
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "noQualifier"));
assertThat(metadata.getBeanName()).isNull();
}
@Test
void isEqualToWithSameInstance() {
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "noQualifier"));
assertThat(metadata).isEqualTo(metadata);
assertThat(metadata).hasSameHashCodeAs(metadata);
}
@Test
void isEqualToWithSameMetadata() {
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "noQualifier"));
OverrideMetadata metadata2 = createMetadata(field(ConfigA.class, "noQualifier"));
assertThat(metadata).isEqualTo(metadata2);
assertThat(metadata).hasSameHashCodeAs(metadata2);
}
@Test
void isEqualToWithSameMetadataAndBeanNames() {
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "noQualifier"), "testBean");
OverrideMetadata metadata2 = createMetadata(field(ConfigA.class, "noQualifier"), "testBean");
assertThat(metadata).isEqualTo(metadata2);
assertThat(metadata).hasSameHashCodeAs(metadata2);
}
@Test
void isNotEqualToWithSameMetadataAndDifferentBeaName() {
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "noQualifier"), "testBean");
OverrideMetadata metadata2 = createMetadata(field(ConfigA.class, "noQualifier"), "testBean2");
assertThat(metadata).isNotEqualTo(metadata2);
}
@Test
void isEqualToWithSameMetadataButDifferentFields() {
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "noQualifier"));
OverrideMetadata metadata2 = createMetadata(field(ConfigB.class, "noQualifier"));
assertThat(metadata).isEqualTo(metadata2);
assertThat(metadata).hasSameHashCodeAs(metadata2);
}
@Test
void isEqualToWithByNameLookupAndDifferentFieldNames() {
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "noQualifier"), "beanToOverride");
OverrideMetadata metadata2 = createMetadata(field(ConfigB.class, "example"), "beanToOverride");
assertThat(metadata).isEqualTo(metadata2);
assertThat(metadata).hasSameHashCodeAs(metadata2);
}
@Test
void isEqualToWithSameMetadataAndSameQualifierValues() {
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "directQualifier"));
OverrideMetadata metadata2 = createMetadata(field(ConfigB.class, "directQualifier"));
assertThat(metadata).isEqualTo(metadata2);
assertThat(metadata).hasSameHashCodeAs(metadata2);
}
@Test
void isEqualToWithSameMetadataAndSameQualifierValuesButWithAnnotationsDeclaredInDifferentOrder() {
Field field1 = field(ConfigA.class, "qualifiedDummyBean");
Field field2 = field(ConfigB.class, "qualifiedDummyBean");
// Prerequisite
assertThat(Arrays.equals(field1.getAnnotations(), field2.getAnnotations())).isFalse();
OverrideMetadata metadata1 = createMetadata(field1);
OverrideMetadata metadata2 = createMetadata(field2);
assertThat(metadata1).isEqualTo(metadata2);
assertThat(metadata1).hasSameHashCodeAs(metadata2);
}
@Test
void isNotEqualToWithSameMetadataAndDifferentQualifierValues() {
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "directQualifier"));
OverrideMetadata metadata2 = createMetadata(field(ConfigA.class, "differentDirectQualifier"));
assertThat(metadata).isNotEqualTo(metadata2);
}
@Test
void isNotEqualToWithSameMetadataAndDifferentQualifiers() {
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "directQualifier"));
OverrideMetadata metadata2 = createMetadata(field(ConfigA.class, "customQualifier"));
assertThat(metadata).isNotEqualTo(metadata2);
}
@Test
void isNotEqualToWithByTypeLookupAndDifferentFieldNames() {
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "noQualifier"));
OverrideMetadata metadata2 = createMetadata(field(ConfigB.class, "example"));
assertThat(metadata).isNotEqualTo(metadata2);
}
private static OverrideMetadata createMetadata(Field field) {
return createMetadata(field, null);
}
private static OverrideMetadata createMetadata(Field field, @Nullable String name) {
return new DummyOverrideMetadata(field, field.getType(), name, BeanOverrideStrategy.REPLACE);
}
private static Field field(Class<?> target, String fieldName) {
Field field = ReflectionUtils.findField(target, fieldName);
assertThat(field).isNotNull();
return field;
}
private static Consumer<OverrideMetadata> hasOverrideMetadata(Field field, Class<?> beanType, @Nullable String beanName) {
return hasOverrideMetadata(field, beanType, BeanOverrideStrategy.REPLACE, beanName);
}
private static Consumer<OverrideMetadata> hasOverrideMetadata(Field field, Class<?> beanType, BeanOverrideStrategy strategy,
@Nullable String beanName) {
return metadata -> {
assertThat(metadata.getField()).isEqualTo(field);
assertThat(metadata.getBeanType().toClass()).isEqualTo(beanType);
assertThat(metadata.getStrategy()).isEqualTo(strategy);
assertThat(metadata.getBeanName()).isEqualTo(beanName);
};
}
static class SingleAnnotation {
@DummyBean
String message;
}
static class MultipleAnnotations {
@DummyBean
String message;
@DummyBean
Integer counter;
}
static class MultipleAnnotationsDuplicate {
@DummyBean(beanName = "messageBean")
String message1;
@DummyBean(beanName = "messageBean")
String message2;
}
static class MultipleAnnotationsOnSameField {
@MetaDummyBean()
@DummyBean
String message;
static String foo() {
return "foo";
}
}
static class ConfigA {
ExampleService noQualifier;
@Qualifier("test")
ExampleService directQualifier;
@Qualifier("different")
ExampleService differentDirectQualifier;
@CustomQualifier
ExampleService customQualifier;
@DummyBean
@Qualifier("test")
ExampleService qualifiedDummyBean;
}
static class ConfigB {
ExampleService noQualifier;
ExampleService example;
@Qualifier("test")
ExampleService directQualifier;
@Qualifier("test")
@DummyBean
ExampleService qualifiedDummyBean;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@DummyBean
@interface MetaDummyBean {}
}

View File

@ -24,8 +24,8 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.ResolvableType;
import org.springframework.test.context.bean.override.BeanOverrideHandler;
import org.springframework.test.context.bean.override.BeanOverrideStrategy;
import org.springframework.test.context.bean.override.OverrideMetadata;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
@ -33,81 +33,81 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Tests for {@link TestBeanOverrideMetadata}.
* Tests for {@link TestBeanBeanOverrideHandler}.
*
* @author Stephane Nicoll
*/
class TestBeanOverrideMetadataTests {
class TestBeanBeanOverrideHandlerTests {
@Test
void forTestClassSetsNameToNullIfAnnotationNameIsNull() {
List<OverrideMetadata> list = OverrideMetadata.forTestClass(SampleOneOverride.class);
assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isNull());
List<BeanOverrideHandler> list = BeanOverrideHandler.forTestClass(SampleOneOverride.class);
assertThat(list).singleElement().satisfies(handler -> assertThat(handler.getBeanName()).isNull());
}
@Test
void forTestClassSetsNameToAnnotationName() {
List<OverrideMetadata> list = OverrideMetadata.forTestClass(SampleOneOverrideWithName.class);
assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isEqualTo("anotherBean"));
List<BeanOverrideHandler> list = BeanOverrideHandler.forTestClass(SampleOneOverrideWithName.class);
assertThat(list).singleElement().satisfies(handler -> assertThat(handler.getBeanName()).isEqualTo("anotherBean"));
}
@Test
void forTestClassWithMissingMethod() {
assertThatIllegalStateException()
.isThrownBy(() ->OverrideMetadata.forTestClass(SampleMissingMethod.class))
.isThrownBy(() ->BeanOverrideHandler.forTestClass(SampleMissingMethod.class))
.withMessage("No static method found named message() in %s with return type %s",
SampleMissingMethod.class.getName(), String.class.getName());
}
@Test
void isEqualToWithSameInstance() {
TestBeanOverrideMetadata metadata = createMetadata(sampleField("message"), sampleMethod("message"));
assertThat(metadata).isEqualTo(metadata);
assertThat(metadata).hasSameHashCodeAs(metadata);
TestBeanBeanOverrideHandler handler = createBeanOverrideHandler(sampleField("message"), sampleMethod("message"));
assertThat(handler).isEqualTo(handler);
assertThat(handler).hasSameHashCodeAs(handler);
}
@Test
void isEqualToWithSameMetadata() {
TestBeanOverrideMetadata metadata1 = createMetadata(sampleField("message"), sampleMethod("message"));
TestBeanOverrideMetadata metadata2 = createMetadata(sampleField("message"), sampleMethod("message"));
assertThat(metadata1).isEqualTo(metadata2);
assertThat(metadata1).hasSameHashCodeAs(metadata2);
TestBeanBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("message"), sampleMethod("message"));
TestBeanBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("message"), sampleMethod("message"));
assertThat(handler1).isEqualTo(handler2);
assertThat(handler1).hasSameHashCodeAs(handler2);
}
@Test
void isEqualToWithSameMetadataByNameLookupAndDifferentField() {
TestBeanOverrideMetadata metadata1 = createMetadata(sampleField("message3"), sampleMethod("message"));
TestBeanOverrideMetadata metadata2 = createMetadata(sampleField("message4"), sampleMethod("message"));
assertThat(metadata1).isEqualTo(metadata2);
assertThat(metadata1).hasSameHashCodeAs(metadata2);
TestBeanBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("message3"), sampleMethod("message"));
TestBeanBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("message4"), sampleMethod("message"));
assertThat(handler1).isEqualTo(handler2);
assertThat(handler1).hasSameHashCodeAs(handler2);
}
@Test
void isNotEqualToWithSameMetadataByTypeLookupAndDifferentField() {
TestBeanOverrideMetadata metadata1 = createMetadata(sampleField("message"), sampleMethod("message"));
TestBeanOverrideMetadata metadata2 = createMetadata(sampleField("message2"), sampleMethod("message"));
assertThat(metadata1).isNotEqualTo(metadata2);
TestBeanBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("message"), sampleMethod("message"));
TestBeanBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("message2"), sampleMethod("message"));
assertThat(handler1).isNotEqualTo(handler2);
}
@Test
void isNotEqualToWithSameMetadataButDifferentBeanName() {
TestBeanOverrideMetadata metadata1 = createMetadata(sampleField("message"), sampleMethod("message"));
TestBeanOverrideMetadata metadata2 = createMetadata(sampleField("message3"), sampleMethod("message"));
assertThat(metadata1).isNotEqualTo(metadata2);
TestBeanBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("message"), sampleMethod("message"));
TestBeanBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("message3"), sampleMethod("message"));
assertThat(handler1).isNotEqualTo(handler2);
}
@Test
void isNotEqualToWithSameMetadataButDifferentMethod() {
TestBeanOverrideMetadata metadata1 = createMetadata(sampleField("message"), sampleMethod("message"));
TestBeanOverrideMetadata metadata2 = createMetadata(sampleField("message"), sampleMethod("description"));
assertThat(metadata1).isNotEqualTo(metadata2);
TestBeanBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("message"), sampleMethod("message"));
TestBeanBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("message"), sampleMethod("description"));
assertThat(handler1).isNotEqualTo(handler2);
}
@Test
void isNotEqualToWithSameMetadataButDifferentAnnotations() {
TestBeanOverrideMetadata metadata1 = createMetadata(sampleField("message"), sampleMethod("message"));
TestBeanOverrideMetadata metadata2 = createMetadata(sampleField("message5"), sampleMethod("message"));
assertThat(metadata1).isNotEqualTo(metadata2);
TestBeanBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("message"), sampleMethod("message"));
TestBeanBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("message5"), sampleMethod("message"));
assertThat(handler1).isNotEqualTo(handler2);
}
private Field sampleField(String fieldName) {
@ -122,10 +122,10 @@ class TestBeanOverrideMetadataTests {
return method;
}
private TestBeanOverrideMetadata createMetadata(Field field, Method overrideMethod) {
private TestBeanBeanOverrideHandler createBeanOverrideHandler(Field field, Method overrideMethod) {
TestBean annotation = field.getAnnotation(TestBean.class);
String beanName = (StringUtils.hasText(annotation.name()) ? annotation.name() : null);
return new TestBeanOverrideMetadata(
return new TestBeanBeanOverrideHandler(
field, ResolvableType.forClass(field.getType()), beanName, BeanOverrideStrategy.REPLACE, overrideMethod);
}

View File

@ -151,7 +151,7 @@ class TestBeanOverrideProcessorTests {
}
@Test
void createMetaDataForUnknownExplicitMethod() throws Exception {
void createBeanOverrideHandlerForUnknownExplicitMethod() throws Exception {
Class<?> clazz = ExplicitMethodNameTestCase.class;
Class<?> returnType = ExampleService.class;
Field field = clazz.getField("a");
@ -159,24 +159,24 @@ class TestBeanOverrideProcessorTests {
assertThat(overrideAnnotation).isNotNull();
assertThatIllegalStateException()
.isThrownBy(() -> this.processor.createMetadata(overrideAnnotation, clazz, field))
.isThrownBy(() -> this.processor.createHandler(overrideAnnotation, clazz, field))
.withMessage("No static method found named explicit1() in %s with return type %s",
clazz.getName(), returnType.getName());
}
@Test
void createMetaDataForKnownExplicitMethod() throws Exception {
void createBeanOverrideHandlerForKnownExplicitMethod() throws Exception {
Class<?> clazz = ExplicitMethodNameTestCase.class;
Field field = clazz.getField("b");
TestBean overrideAnnotation = field.getAnnotation(TestBean.class);
assertThat(overrideAnnotation).isNotNull();
assertThat(this.processor.createMetadata(overrideAnnotation, clazz, field))
.isInstanceOf(TestBeanOverrideMetadata.class);
assertThat(this.processor.createHandler(overrideAnnotation, clazz, field))
.isInstanceOf(TestBeanBeanOverrideHandler.class);
}
@Test
void createMetaDataForConventionBasedFactoryMethod() throws Exception {
void createBeanOverrideHandlerForConventionBasedFactoryMethod() throws Exception {
Class<?> returnType = ExampleService.class;
Class<?> clazz = MethodConventionTestCase.class;
Field field = clazz.getField("field");
@ -184,19 +184,19 @@ class TestBeanOverrideProcessorTests {
assertThat(overrideAnnotation).isNotNull();
assertThatIllegalStateException()
.isThrownBy(() -> this.processor.createMetadata(overrideAnnotation, clazz, field))
.isThrownBy(() -> this.processor.createHandler(overrideAnnotation, clazz, field))
.withMessage("No static method found named field() or someField() in %s with return type %s",
clazz.getName(), returnType.getName());
}
@Test
void failToCreateMetadataForOtherAnnotation() throws NoSuchFieldException {
void failToCreateBeanOverrideHandlerForOtherAnnotation() throws Exception {
Class<?> clazz = MethodConventionTestCase.class;
Field field = clazz.getField("field");
NonNull badAnnotation = AnnotationUtils.synthesizeAnnotation(NonNull.class);
assertThatIllegalStateException()
.isThrownBy(() -> this.processor.createMetadata(badAnnotation, clazz, field))
.isThrownBy(() -> this.processor.createHandler(badAnnotation, clazz, field))
.withMessage("Invalid annotation passed to TestBeanOverrideProcessor: expected @TestBean" +
" on field %s.%s", field.getDeclaringClass().getName(), field.getName());
}

View File

@ -25,22 +25,22 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.test.context.bean.override.OverrideMetadata;
import org.springframework.test.context.bean.override.BeanOverrideHandler;
import static org.springframework.test.context.bean.override.BeanOverrideStrategy.REPLACE_OR_CREATE;
/**
* {@link OverrideMetadata} that provides support for {@link EasyMockBean @EasyMockBean}.
* {@link BeanOverrideHandler} that provides support for {@link EasyMockBean @EasyMockBean}.
*
* @author Sam Brannen
* @since 6.2
*/
class EasyMockBeanOverrideMetadata extends OverrideMetadata {
class EasyMockBeanOverrideHandler extends BeanOverrideHandler {
private final MockType mockType;
EasyMockBeanOverrideMetadata(Field field, Class<?> typeToOverride, @Nullable String beanName,
EasyMockBeanOverrideHandler(Field field, Class<?> typeToOverride, @Nullable String beanName,
MockType mockType) {
super(field, ResolvableType.forClass(typeToOverride), beanName, REPLACE_OR_CREATE);
@ -49,7 +49,7 @@ class EasyMockBeanOverrideMetadata extends OverrideMetadata {
@Override
protected Object createOverride(String beanName, @Nullable BeanDefinition existingBeanDefinition,
protected Object createOverrideInstance(String beanName, @Nullable BeanDefinition existingBeanDefinition,
@Nullable Object existingBeanInstance) {
Class<?> typeToMock = getBeanType().getRawClass();
@ -57,7 +57,7 @@ class EasyMockBeanOverrideMetadata extends OverrideMetadata {
}
@Override
protected void track(Object mock, SingletonBeanRegistry singletonBeanRegistry) {
protected void trackOverrideInstance(Object mock, SingletonBeanRegistry singletonBeanRegistry) {
getEasyMockBeans(singletonBeanRegistry).add(mock);
}

View File

@ -19,8 +19,8 @@ package org.springframework.test.context.bean.override.easymock;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import org.springframework.test.context.bean.override.BeanOverrideHandler;
import org.springframework.test.context.bean.override.BeanOverrideProcessor;
import org.springframework.test.context.bean.override.OverrideMetadata;
import org.springframework.util.StringUtils;
/**
@ -32,10 +32,10 @@ import org.springframework.util.StringUtils;
class EasyMockBeanOverrideProcessor implements BeanOverrideProcessor {
@Override
public OverrideMetadata createMetadata(Annotation annotation, Class<?> testClass, Field field) {
public BeanOverrideHandler createHandler(Annotation annotation, Class<?> testClass, Field field) {
EasyMockBean easyMockBean = (EasyMockBean) annotation;
String beanName = (StringUtils.hasText(easyMockBean.name()) ? easyMockBean.name() : null);
return new EasyMockBeanOverrideMetadata(field, field.getType(), beanName, easyMockBean.mockType());
return new EasyMockBeanOverrideHandler(field, field.getType(), beanName, easyMockBean.mockType());
}
}

View File

@ -25,86 +25,86 @@ import org.mockito.Answers;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.test.context.bean.override.OverrideMetadata;
import org.springframework.test.context.bean.override.BeanOverrideHandler;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link MockitoBeanOverrideMetadata}.
* Tests for {@link MockitoBeanOverrideHandler}.
*
* @author Stephane Nicoll
*/
class MockitoBeanOverrideMetadataTests {
class MockitoBeanOverrideHandlerTests {
@Test
void forTestClassSetsNameToNullIfAnnotationNameIsNull() {
List<OverrideMetadata> list = OverrideMetadata.forTestClass(SampleOneMock.class);
assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isNull());
List<BeanOverrideHandler> list = BeanOverrideHandler.forTestClass(SampleOneMock.class);
assertThat(list).singleElement().satisfies(handler -> assertThat(handler.getBeanName()).isNull());
}
@Test
void forTestClassSetsNameToAnnotationName() {
List<OverrideMetadata> list = OverrideMetadata.forTestClass(SampleOneMockWithName.class);
assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isEqualTo("anotherService"));
List<BeanOverrideHandler> list = BeanOverrideHandler.forTestClass(SampleOneMockWithName.class);
assertThat(list).singleElement().satisfies(handler -> assertThat(handler.getBeanName()).isEqualTo("anotherService"));
}
@Test
void isEqualToWithSameInstance() {
MockitoBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
assertThat(metadata).isEqualTo(metadata);
assertThat(metadata).hasSameHashCodeAs(metadata);
MockitoBeanOverrideHandler handler = createBeanOverrideHandler(sampleField("service"));
assertThat(handler).isEqualTo(handler);
assertThat(handler).hasSameHashCodeAs(handler);
}
@Test
void isEqualToWithSameMetadata() {
MockitoBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
MockitoBeanOverrideMetadata metadata2 = createMetadata(sampleField("service"));
assertThat(metadata).isEqualTo(metadata2);
assertThat(metadata).hasSameHashCodeAs(metadata2);
MockitoBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("service"));
MockitoBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("service"));
assertThat(handler1).isEqualTo(handler2);
assertThat(handler1).hasSameHashCodeAs(handler2);
}
@Test
void isNotEqualEqualToByTypeLookupWithSameMetadataButDifferentField() {
MockitoBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
MockitoBeanOverrideMetadata metadata2 = createMetadata(sampleField("service2"));
assertThat(metadata).isNotEqualTo(metadata2);
MockitoBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("service"));
MockitoBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("service2"));
assertThat(handler1).isNotEqualTo(handler2);
}
@Test
void isEqualEqualToByNameLookupWithSameMetadataButDifferentField() {
MockitoBeanOverrideMetadata metadata = createMetadata(sampleField("service3"));
MockitoBeanOverrideMetadata metadata2 = createMetadata(sampleField("service4"));
assertThat(metadata).isEqualTo(metadata2);
assertThat(metadata).hasSameHashCodeAs(metadata2);
MockitoBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("service3"));
MockitoBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("service4"));
assertThat(handler1).isEqualTo(handler2);
assertThat(handler1).hasSameHashCodeAs(handler2);
}
@Test
void isNotEqualToWithSameMetadataButDifferentBeanName() {
MockitoBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
MockitoBeanOverrideMetadata metadata2 = createMetadata(sampleField("service3"));
assertThat(metadata).isNotEqualTo(metadata2);
MockitoBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("service"));
MockitoBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("service3"));
assertThat(handler1).isNotEqualTo(handler2);
}
@Test
void isNotEqualToWithSameMetadataButDifferentExtraInterfaces() {
MockitoBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
MockitoBeanOverrideMetadata metadata2 = createMetadata(sampleField("service5"));
assertThat(metadata).isNotEqualTo(metadata2);
MockitoBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("service"));
MockitoBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("service5"));
assertThat(handler1).isNotEqualTo(handler2);
}
@Test
void isNotEqualToWithSameMetadataButDifferentAnswers() {
MockitoBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
MockitoBeanOverrideMetadata metadata2 = createMetadata(sampleField("service6"));
assertThat(metadata).isNotEqualTo(metadata2);
MockitoBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("service"));
MockitoBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("service6"));
assertThat(handler1).isNotEqualTo(handler2);
}
@Test
void isNotEqualToWithSameMetadataButDifferentSerializableFlag() {
MockitoBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
MockitoBeanOverrideMetadata metadata2 = createMetadata(sampleField("service7"));
assertThat(metadata).isNotEqualTo(metadata2);
MockitoBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("service"));
MockitoBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("service7"));
assertThat(handler1).isNotEqualTo(handler2);
}
@ -114,9 +114,9 @@ class MockitoBeanOverrideMetadataTests {
return field;
}
private MockitoBeanOverrideMetadata createMetadata(Field field) {
private MockitoBeanOverrideHandler createBeanOverrideHandler(Field field) {
MockitoBean annotation = AnnotatedElementUtils.getMergedAnnotation(field, MockitoBean.class);
return new MockitoBeanOverrideMetadata(field, ResolvableType.forClass(field.getType()), annotation);
return new MockitoBeanOverrideHandler(field, ResolvableType.forClass(field.getType()), annotation);
}

View File

@ -23,7 +23,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
import org.springframework.test.context.bean.override.OverrideMetadata;
import org.springframework.test.context.bean.override.BeanOverrideHandler;
import org.springframework.test.context.bean.override.example.ExampleService;
import static org.assertj.core.api.Assertions.assertThat;
@ -37,23 +37,23 @@ public class MockitoBeanOverrideProcessorTests {
private final MockitoBeanOverrideProcessor processor = new MockitoBeanOverrideProcessor();
@Test
void mockAnnotationCreatesMockMetadata() throws NoSuchFieldException {
void mockAnnotationCreatesMockitoBeanOverrideHandler() throws NoSuchFieldException {
MockitoBean annotation = AnnotationUtils.synthesizeAnnotation(MockitoBean.class);
Class<?> clazz = MockitoConf.class;
Field field = clazz.getField("a");
OverrideMetadata object = this.processor.createMetadata(annotation, clazz, field);
BeanOverrideHandler object = this.processor.createHandler(annotation, clazz, field);
assertThat(object).isExactlyInstanceOf(MockitoBeanOverrideMetadata.class);
assertThat(object).isExactlyInstanceOf(MockitoBeanOverrideHandler.class);
}
@Test
void spyAnnotationCreatesSpyMetadata() throws NoSuchFieldException {
void spyAnnotationCreatesMockitoSpyBeanOverrideHandler() throws NoSuchFieldException {
MockitoSpyBean annotation = AnnotationUtils.synthesizeAnnotation(MockitoSpyBean.class);
Class<?> clazz = MockitoConf.class;
Field field = clazz.getField("a");
OverrideMetadata object = this.processor.createMetadata(annotation, clazz, field);
BeanOverrideHandler object = this.processor.createHandler(annotation, clazz, field);
assertThat(object).isExactlyInstanceOf(MockitoSpyBeanOverrideMetadata.class);
assertThat(object).isExactlyInstanceOf(MockitoSpyBeanOverrideHandler.class);
}
@Test
@ -63,7 +63,7 @@ public class MockitoBeanOverrideProcessorTests {
Annotation annotation = field.getAnnotation(Nullable.class);
assertThatIllegalStateException()
.isThrownBy(() -> this.processor.createMetadata(annotation, clazz, field))
.isThrownBy(() -> this.processor.createHandler(annotation, clazz, field))
.withMessage("Invalid annotation passed to MockitoBeanOverrideProcessor: expected either " +
"@MockitoBean or @MockitoSpyBean on field %s.%s", field.getDeclaringClass().getName(),
field.getName());

View File

@ -32,6 +32,7 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
/**
* Test {@link MockitoSpyBean @MockitoSpyBean} for a factory bean configuration.
@ -57,12 +58,12 @@ class MockitoSpyBeanForFactoryBeanIntegrationTests {
TestBean bean = this.applicationContext.getBean(TestBean.class);
assertThat(this.testBean).as("injected same").isSameAs(bean);
assertThat(bean.hello()).isEqualTo("hi");
Mockito.verify(bean).hello();
verify(bean).hello();
doReturn("sp-hi").when(this.testBean).hello();
assertThat(bean.hello()).as("after stubbing").isEqualTo("sp-hi");
Mockito.verify(bean, Mockito.times(2)).hello();
verify(bean, Mockito.times(2)).hello();
}
@Order(2)

View File

@ -23,79 +23,79 @@ import org.junit.jupiter.api.Test;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.test.context.bean.override.OverrideMetadata;
import org.springframework.test.context.bean.override.BeanOverrideHandler;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link MockitoSpyBeanOverrideMetadata}.
* Tests for {@link MockitoSpyBeanOverrideHandler}.
*
* @author Stephane Nicoll
*/
class MockitoSpyBeanOverrideMetadataTests {
class MockitoSpyBeanOverrideHandlerTests {
@Test
void forTestClassSetsNameToNullIfAnnotationNameIsNull() {
List<OverrideMetadata> list = OverrideMetadata.forTestClass(SampleOneSpy.class);
assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isNull());
List<BeanOverrideHandler> list = BeanOverrideHandler.forTestClass(SampleOneSpy.class);
assertThat(list).singleElement().satisfies(handler -> assertThat(handler.getBeanName()).isNull());
}
@Test
void forTestClassSetsNameToAnnotationName() {
List<OverrideMetadata> list = OverrideMetadata.forTestClass(SampleOneSpyWithName.class);
assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isEqualTo("anotherService"));
List<BeanOverrideHandler> list = BeanOverrideHandler.forTestClass(SampleOneSpyWithName.class);
assertThat(list).singleElement().satisfies(handler -> assertThat(handler.getBeanName()).isEqualTo("anotherService"));
}
@Test
void isEqualToWithSameInstance() {
MockitoSpyBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
assertThat(metadata).isEqualTo(metadata);
assertThat(metadata).hasSameHashCodeAs(metadata);
MockitoSpyBeanOverrideHandler handler = createBeanOverrideHandler(sampleField("service"));
assertThat(handler).isEqualTo(handler);
assertThat(handler).hasSameHashCodeAs(handler);
}
@Test
void isEqualToWithSameMetadata() {
MockitoSpyBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
MockitoSpyBeanOverrideMetadata metadata2 = createMetadata(sampleField("service"));
assertThat(metadata).isEqualTo(metadata2);
assertThat(metadata).hasSameHashCodeAs(metadata2);
MockitoSpyBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("service"));
MockitoSpyBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("service"));
assertThat(handler1).isEqualTo(handler2);
assertThat(handler1).hasSameHashCodeAs(handler2);
}
@Test
void isNotEqualToByTypeLookupWithSameMetadataButDifferentField() {
MockitoSpyBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
MockitoSpyBeanOverrideMetadata metadata2 = createMetadata(sampleField("service2"));
assertThat(metadata).isNotEqualTo(metadata2);
MockitoSpyBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("service"));
MockitoSpyBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("service2"));
assertThat(handler1).isNotEqualTo(handler2);
}
@Test
void isEqualToByNameLookupWithSameMetadataButDifferentField() {
MockitoSpyBeanOverrideMetadata metadata = createMetadata(sampleField("service3"));
MockitoSpyBeanOverrideMetadata metadata2 = createMetadata(sampleField("service4"));
assertThat(metadata).isEqualTo(metadata2);
assertThat(metadata).hasSameHashCodeAs(metadata2);
MockitoSpyBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("service3"));
MockitoSpyBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("service4"));
assertThat(handler1).isEqualTo(handler2);
assertThat(handler1).hasSameHashCodeAs(handler2);
}
@Test
void isNotEqualToWithSameMetadataButDifferentBeanName() {
MockitoSpyBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
MockitoSpyBeanOverrideMetadata metadata2 = createMetadata(sampleField("service3"));
assertThat(metadata).isNotEqualTo(metadata2);
MockitoSpyBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("service"));
MockitoSpyBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("service3"));
assertThat(handler1).isNotEqualTo(handler2);
}
@Test
void isNotEqualToWithSameMetadataButDifferentReset() {
MockitoSpyBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
MockitoSpyBeanOverrideMetadata metadata2 = createMetadata(sampleField("service5"));
assertThat(metadata).isNotEqualTo(metadata2);
MockitoSpyBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("service"));
MockitoSpyBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("service5"));
assertThat(handler1).isNotEqualTo(handler2);
}
@Test
void isNotEqualToWithSameMetadataButDifferentProxyTargetAwareFlag() {
MockitoSpyBeanOverrideMetadata metadata = createMetadata(sampleField("service"));
MockitoSpyBeanOverrideMetadata metadata2 = createMetadata(sampleField("service6"));
assertThat(metadata).isNotEqualTo(metadata2);
MockitoSpyBeanOverrideHandler handler1 = createBeanOverrideHandler(sampleField("service"));
MockitoSpyBeanOverrideHandler handler2 = createBeanOverrideHandler(sampleField("service6"));
assertThat(handler1).isNotEqualTo(handler2);
}
@ -105,9 +105,9 @@ class MockitoSpyBeanOverrideMetadataTests {
return field;
}
private MockitoSpyBeanOverrideMetadata createMetadata(Field field) {
private MockitoSpyBeanOverrideHandler createBeanOverrideHandler(Field field) {
MockitoSpyBean annotation = AnnotatedElementUtils.getMergedAnnotation(field, MockitoSpyBean.class);
return new MockitoSpyBeanOverrideMetadata(field, ResolvableType.forClass(field.getType()), annotation);
return new MockitoSpyBeanOverrideHandler(field, ResolvableType.forClass(field.getType()), annotation);
}