diff --git a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/bean-overriding.adoc b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/bean-overriding.adoc index 02d78d23f7..5af9e033e0 100644 --- a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/bean-overriding.adoc +++ b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/bean-overriding.adoc @@ -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 diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java index 89729a7d3c..5fb19c6453 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java @@ -47,12 +47,12 @@ import org.springframework.util.Assert; * accordingly. * *

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. * - *

This processor does not work against a particular test class, but rather + *

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 metadata; + private final Set 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 metadata, BeanOverrideRegistrar overrideRegistrar) { - this.metadata = metadata; - this.overrideRegistrar = overrideRegistrar; + BeanOverrideBeanFactoryPostProcessor(Set 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 candidates = getExistingBeanNamesByType(beanFactory, overrideMetadata, false); + Set 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. - *

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. + *

If so, put the {@link BeanOverrideHandler} in the early tracking map. *

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 candidateNames = getExistingBeanNamesByType(beanFactory, overrideMetadata, true); + Set 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 candidates = getExistingBeanNamesByType(beanFactory, overrideMetadata, false); + Set 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 candidateNames = getExistingBeanNamesByType(beanFactory, overrideMetadata, true); + Set 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 getExistingBeanNamesByType(ConfigurableListableBeanFactory beanFactory, OverrideMetadata metadata, + private Set 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. *

The returned bean definition should not be used to create * a bean instance but rather only for the purpose of having suitable bean * definition metadata available in the {@link BeanFactory} — 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; } diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizer.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizer.java index 3753f348ea..134b493e17 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizer.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizer.java @@ -44,10 +44,10 @@ class BeanOverrideContextCustomizer implements ContextCustomizer { "org.springframework.test.context.bean.override.internalWrapEarlyBeanPostProcessor"; - private final Set metadata; + private final Set handlers; - BeanOverrideContextCustomizer(Set metadata) { - this.metadata = metadata; + BeanOverrideContextCustomizer(Set 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 argument that it accepts in its constructor. + // Set 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 getMetadata() { - return this.metadata; + Set 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(); } } diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizerFactory.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizerFactory.java index 4b79f97d45..764f7cedc7 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizerFactory.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizerFactory.java @@ -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 configAttributes) { - Set metadata = new HashSet<>(); - findOverrideMetadata(testClass, metadata); - if (metadata.isEmpty()) { + Set 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 metadata) { - List overrideMetadata = OverrideMetadata.forTestClass(testClass); - metadata.addAll(overrideMetadata); + private void findBeanOverrideHandler(Class testClass, Set handlers) { + handlers.addAll(BeanOverrideHandler.forTestClass(testClass)); if (TestContextAnnotationUtils.searchEnclosingClass(testClass)) { - findOverrideMetadata(testClass.getEnclosingClass(), metadata); + findBeanOverrideHandler(testClass.getEnclosingClass(), handlers); } } diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/OverrideMetadata.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideHandler.java similarity index 67% rename from spring-test/src/main/java/org/springframework/test/context/bean/override/OverrideMetadata.java rename to spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideHandler.java index 4b7da91608..91bac5500a 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/OverrideMetadata.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideHandler.java @@ -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. * - *

WARNING: implementations are used as a cache key and - * must implement proper {@code equals()} and {@code hashCode()} methods. + *

WARNING: 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. * - *

Concrete implementations of {@code OverrideMetadata} can store state to use - * during override {@linkplain #createOverride(String, BeanDefinition, Object) - * instance creation} — for example, based on further processing of the - * annotation or the annotated field. + *

Concrete implementations of {@code BeanOverrideHandler} can store additional + * metadata to use during override {@linkplain #createOverrideInstance instance + * creation} — for example, based on further processing of the annotation + * or the annotated field. * *

NOTE: Only singleton 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 forTestClass(Class testClass) { - List metadata = new LinkedList<>(); - ReflectionUtils.doWithFields(testClass, field -> processField(field, testClass, metadata)); - return metadata; + public static List forTestClass(Class testClass) { + List handlers = new LinkedList<>(); + ReflectionUtils.doWithFields(testClass, field -> processField(field, testClass, handlers)); + return handlers; } - private static void processField(Field field, Class testClass, List metadataList) { + private static void processField(Field field, Class testClass, List 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}. *

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)) { diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideProcessor.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideProcessor.java index c157002494..7b1b654d94 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideProcessor.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideProcessor.java @@ -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. * *

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}. * *

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); } diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideRegistrar.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideRegistrar.java index 2269dbc04b..54ab0cc156 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideRegistrar.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideRegistrar.java @@ -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 beanNameRegistry = new HashMap<>(); + private final Map beanNameRegistry = new HashMap<>(); - private final Map earlyOverrideMetadata = new HashMap<>(); + private final Map 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) { diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideTestExecutionListener.java index 9470291c9a..9861ef587c 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideTestExecutionListener.java @@ -77,14 +77,14 @@ public class BeanOverrideTestExecutionListener extends AbstractTestExecutionList * a corresponding bean override instance. */ private static void injectFields(TestContext testContext) { - List overrideMetadataList = OverrideMetadata.forTestClass(testContext.getTestClass()); - if (!overrideMetadataList.isEmpty()) { + List 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); } } } diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideMetadata.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanBeanOverrideHandler.java similarity index 67% rename from spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideMetadata.java rename to spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanBeanOverrideHandler.java index ef095f5ba7..9f7fc255dc 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideMetadata.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanBeanOverrideHandler.java @@ -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(); } } diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessor.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessor.java index b533aea558..39fff404a7 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessor.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessor.java @@ -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); } /** diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/AbstractMockitoOverrideMetadata.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/AbstractMockitoBeanOverrideHandler.java similarity index 84% rename from spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/AbstractMockitoOverrideMetadata.java rename to spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/AbstractMockitoBeanOverrideHandler.java index 6c31e4fae2..aa5d79be02 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/AbstractMockitoOverrideMetadata.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/AbstractMockitoBeanOverrideHandler.java @@ -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)); } diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideMetadata.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideHandler.java similarity index 87% rename from spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideMetadata.java rename to spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideHandler.java index 0037ceee24..fb757f48eb 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideMetadata.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideHandler.java @@ -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> 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)); } diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideProcessor.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideProcessor.java index 16c16411ba..fd4e3d389b 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideProcessor.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideProcessor.java @@ -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: \ diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideMetadata.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideHandler.java similarity index 86% rename from spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideMetadata.java rename to spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideHandler.java index f3c5a2714d..d61a1a796d 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideMetadata.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideHandler.java @@ -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, diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessorTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessorTests.java index 7e9fc86b0e..505f246710 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessorTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessorTests.java @@ -354,8 +354,8 @@ class BeanOverrideBeanFactoryPostProcessorTests { private AnnotationConfigApplicationContext createContext(Class testClass) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - Set metadata = new LinkedHashSet<>(OverrideMetadata.forTestClass(testClass)); - new BeanOverrideContextCustomizer(metadata).customizeContext(context, mock(MergedContextConfiguration.class)); + Set handlers = new LinkedHashSet<>(BeanOverrideHandler.forTestClass(testClass)); + new BeanOverrideContextCustomizer(handlers).customizeContext(context, mock(MergedContextConfiguration.class)); return context; } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizerFactoryTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizerFactoryTests.java index 61474dcb1f..0dbef0ee7d 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizerFactoryTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizerFactoryTests.java @@ -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 dummyMetadata(@Nullable String beanName, Class beanType) { - return dummyMetadata(beanName, beanType, BeanOverrideStrategy.REPLACE); + private Consumer dummyHandler(@Nullable String beanName, Class beanType) { + return dummyHandler(beanName, beanType, BeanOverrideStrategy.REPLACE); } - private Consumer 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 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 diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizerTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizerTests.java index f8fec2f07a..8944aeb2be 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizerTests.java @@ -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); } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideHandlerTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideHandlerTests.java new file mode 100644 index 0000000000..85b4d0df2f --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideHandlerTests.java @@ -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 handlers = BeanOverrideHandler.forTestClass(SingleAnnotation.class); + assertThat(handlers).singleElement().satisfies(hasBeanOverrideHandler( + field(SingleAnnotation.class, "message"), String.class, null)); + } + + @Test + void forTestClassWithMultipleFields() { + List 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 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 hasBeanOverrideHandler(Field field, Class beanType, @Nullable String beanName) { + return hasBeanOverrideHandler(field, beanType, BeanOverrideStrategy.REPLACE, beanName); + } + + private static Consumer 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 {} + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/DummyBean.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/DummyBean.java index 9004731ba5..d6beaf4ba3 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/DummyBean.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/DummyBean.java @@ -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(); diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/OverrideMetadataTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/OverrideMetadataTests.java deleted file mode 100644 index 8883ca5929..0000000000 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/OverrideMetadataTests.java +++ /dev/null @@ -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.forTestClass(SingleAnnotation.class); - assertThat(overrideMetadata).singleElement().satisfies(hasOverrideMetadata( - field(SingleAnnotation.class, "message"), String.class, null)); - } - - @Test - void forTestClassWithMultipleFields() { - List 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.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 hasOverrideMetadata(Field field, Class beanType, @Nullable String beanName) { - return hasOverrideMetadata(field, beanType, BeanOverrideStrategy.REPLACE, beanName); - } - - private static Consumer 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 {} - -} diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideMetadataTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanBeanOverrideHandlerTests.java similarity index 55% rename from spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideMetadataTests.java rename to spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanBeanOverrideHandlerTests.java index 08ce97aa8a..588a07ef00 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideMetadataTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanBeanOverrideHandlerTests.java @@ -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 list = OverrideMetadata.forTestClass(SampleOneOverride.class); - assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isNull()); + List list = BeanOverrideHandler.forTestClass(SampleOneOverride.class); + assertThat(list).singleElement().satisfies(handler -> assertThat(handler.getBeanName()).isNull()); } @Test void forTestClassSetsNameToAnnotationName() { - List list = OverrideMetadata.forTestClass(SampleOneOverrideWithName.class); - assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isEqualTo("anotherBean")); + List 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); } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessorTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessorTests.java index ee6e196f53..155f896b82 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessorTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessorTests.java @@ -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()); } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/easymock/EasyMockBeanOverrideMetadata.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/easymock/EasyMockBeanOverrideHandler.java similarity index 79% rename from spring-test/src/test/java/org/springframework/test/context/bean/override/easymock/EasyMockBeanOverrideMetadata.java rename to spring-test/src/test/java/org/springframework/test/context/bean/override/easymock/EasyMockBeanOverrideHandler.java index 4ce02a702e..d93fafb783 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/easymock/EasyMockBeanOverrideMetadata.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/easymock/EasyMockBeanOverrideHandler.java @@ -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); } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/easymock/EasyMockBeanOverrideProcessor.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/easymock/EasyMockBeanOverrideProcessor.java index 954c77d0e9..a8f9678d4f 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/easymock/EasyMockBeanOverrideProcessor.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/easymock/EasyMockBeanOverrideProcessor.java @@ -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()); } } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideMetadataTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideHandlerTests.java similarity index 50% rename from spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideMetadataTests.java rename to spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideHandlerTests.java index 0a1a222b5b..6a44a83f13 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideMetadataTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideHandlerTests.java @@ -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 list = OverrideMetadata.forTestClass(SampleOneMock.class); - assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isNull()); + List list = BeanOverrideHandler.forTestClass(SampleOneMock.class); + assertThat(list).singleElement().satisfies(handler -> assertThat(handler.getBeanName()).isNull()); } @Test void forTestClassSetsNameToAnnotationName() { - List list = OverrideMetadata.forTestClass(SampleOneMockWithName.class); - assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isEqualTo("anotherService")); + List 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); } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideProcessorTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideProcessorTests.java index 19feda9f2f..fccb4b25ee 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideProcessorTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideProcessorTests.java @@ -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()); diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanForFactoryBeanIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanForFactoryBeanIntegrationTests.java index 06584b76de..34771c4560 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanForFactoryBeanIntegrationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanForFactoryBeanIntegrationTests.java @@ -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) diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideMetadataTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideHandlerTests.java similarity index 50% rename from spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideMetadataTests.java rename to spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideHandlerTests.java index 4c91f43102..76cd3e8b56 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideMetadataTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideHandlerTests.java @@ -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 list = OverrideMetadata.forTestClass(SampleOneSpy.class); - assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isNull()); + List list = BeanOverrideHandler.forTestClass(SampleOneSpy.class); + assertThat(list).singleElement().satisfies(handler -> assertThat(handler.getBeanName()).isNull()); } @Test void forTestClassSetsNameToAnnotationName() { - List list = OverrideMetadata.forTestClass(SampleOneSpyWithName.class); - assertThat(list).singleElement().satisfies(metadata -> assertThat(metadata.getBeanName()).isEqualTo("anotherService")); + List 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); }