Skip to content

Commit

Permalink
Revise Bean Override internals and Javadoc
Browse files Browse the repository at this point in the history
  • Loading branch information
sbrannen committed Sep 28, 2024
1 parent 56f3a48 commit 40ca83d
Show file tree
Hide file tree
Showing 19 changed files with 151 additions and 146 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.springframework.test.context.bean.override;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
Expand All @@ -24,17 +25,23 @@
/**
* Mark a composed annotation as eligible for Bean Override processing.
*
* <p>Specifying this annotation triggers the configured {@link BeanOverrideProcessor}
* <p>Specifying this annotation registers the configured {@link BeanOverrideProcessor}
* which must be capable of handling the composed annotation and its attributes.
*
* <p>Since the composed annotation should only be applied to fields, it is
* expected that it has a {@link Target} of {@link ElementType#FIELD FIELD}.
* expected that it is meta-annotated with {@link Target @Target(ElementType.FIELD)}.
*
* <p>For concrete examples, see
* {@link org.springframework.test.context.bean.override.convention.TestBean @TestBean},
* {@link org.springframework.test.context.bean.override.mockito.MockitoBean @MockitoBean}, and
* {@link org.springframework.test.context.bean.override.mockito.MockitoSpyBean @MockitoSpyBean}.
*
* @author Simon Baslé
* @since 6.2
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@Documented
public @interface BeanOverride {

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@
*/
class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered {

private static final BeanNameGenerator beanNameGenerator = DefaultBeanNameGenerator.INSTANCE;

private final Set<OverrideMetadata> metadata;

private final BeanOverrideRegistrar overrideRegistrar;

private final BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();


/**
* Create a new {@code BeanOverrideBeanFactoryPostProcessor} with the supplied
Expand Down Expand Up @@ -215,7 +215,7 @@ else if (candidateCount == 0) {
"Unable to override bean: no bean definitions of type %s (as required by annotated field '%s.%s')"
.formatted(overrideMetadata.getBeanType(), field.getDeclaringClass().getSimpleName(), field.getName()));
}
return this.beanNameGenerator.generateBeanName(beanDefinition, registry);
return beanNameGenerator.generateBeanName(beanDefinition, registry);
}

Field field = overrideMetadata.getField();
Expand All @@ -230,7 +230,7 @@ private Set<String> getExistingBeanNamesByType(ConfigurableListableBeanFactory b
boolean checkAutowiredCandidate) {

ResolvableType resolvableType = metadata.getBeanType();
Class<?> type = resolvableType.resolve(Object.class);
Class<?> type = resolvableType.toClass();

// Start with matching bean names for type, excluding FactoryBeans.
Set<String> beanNames = new LinkedHashSet<>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
* <p>At least one composed annotation that is meta-annotated with
* {@link BeanOverride @BeanOverride} must be a companion of this processor and
* may provide additional user settings that drive how the concrete
* {@link OverrideMetadata} is configured.
* {@code OverrideMetadata} is configured.
*
* <p>Implementations are required to have a no-argument constructor and be
* stateless.
Expand All @@ -41,7 +41,7 @@ public interface BeanOverrideProcessor {
/**
* Create an {@link OverrideMetadata} instance for the given annotated field.
* @param overrideAnnotation the composed annotation that declares the
* {@link BeanOverride @BeanOverride} annotation that triggers this processor
* {@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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@
*
* @author Simon Baslé
* @author Stephane Nicoll
* @author Sam Brannen
* @since 6.2
*/
public enum BeanOverrideStrategy {

/**
* Replace a given bean definition, immediately preparing a singleton instance.
* <p>Fails if the original bean definition exists. To create a new bean
* definition in such a case, use {@link #REPLACE_OR_CREATE_DEFINITION}.
* <p>Fails if the original bean definition does not exist. To create a new bean
* definition in such a case, use {@link #REPLACE_OR_CREATE_DEFINITION} instead.
*/
REPLACE_DEFINITION,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@
import org.springframework.test.context.bean.override.BeanOverride;

/**
* Mark a field to override a bean definition in the {@code BeanFactory}.
* {@code @TestBean} is an annotation that can be applied to a field in a test
* class to override a bean in the test's
* {@link org.springframework.context.ApplicationContext ApplicationContext}
* using a static factory method.
*
* <p>By default, the bean to override is inferred from the type of the
* annotated field. This requires that exactly one matching bean definition is
Expand Down Expand Up @@ -57,13 +60,12 @@
*
* <p>Consider the following example.
*
* <pre><code>
* class CustomerServiceTests {
* <pre><code> class CustomerServiceTests {
*
* &#064;TestBean
* private CustomerRepository repository;
*
* // Tests
* // &#064;Test methods ...
*
* private static CustomerRepository repository() {
* return new TestCustomerRepository();
Expand All @@ -79,15 +81,14 @@
* <p>To make things more explicit, the bean and method names can be set,
* as shown in the following example.
*
* <pre><code>
* class CustomerServiceTests {
* <pre><code> class CustomerServiceTests {
*
* &#064;TestBean(name = "customerRepository", methodName = "createTestCustomerRepository")
* private CustomerRepository repository;
* CustomerRepository repository;
*
* // Tests
* // &#064;Test methods ...
*
* private static CustomerRepository createTestCustomerRepository() {
* static CustomerRepository createTestCustomerRepository() {
* return new TestCustomerRepository();
* }
* }</code></pre>
Expand All @@ -96,7 +97,8 @@
* @author Stephane Nicoll
* @author Sam Brannen
* @since 6.2
* @see TestBeanOverrideProcessor
* @see org.springframework.test.context.bean.override.mockito.MockitoBean @MockitoBean
* @see org.springframework.test.context.bean.override.mockito.MockitoSpyBean @MockitoSpyBean
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.lang.reflect.Field;
import java.util.Objects;

import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.core.ResolvableType;
import org.springframework.core.style.ToStringCreator;
Expand All @@ -28,21 +27,21 @@
import org.springframework.test.context.bean.override.OverrideMetadata;

/**
* Base {@link OverrideMetadata} implementation for Mockito.
* Abstract base {@link OverrideMetadata} implementation for Mockito.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @author Sam Brannen
* @since 6.2
*/
abstract class MockitoOverrideMetadata extends OverrideMetadata {
abstract class AbstractMockitoOverrideMetadata extends OverrideMetadata {

private final MockReset reset;

private final boolean proxyTargetAware;


protected MockitoOverrideMetadata(Field field, ResolvableType beanType, @Nullable String beanName,
protected AbstractMockitoOverrideMetadata(Field field, ResolvableType beanType, @Nullable String beanName,
BeanOverrideStrategy strategy, @Nullable MockReset reset, boolean proxyTargetAware) {

super(field, beanType, beanName, strategy);
Expand All @@ -69,25 +68,28 @@ boolean isProxyTargetAware() {

@Override
protected void track(Object mock, SingletonBeanRegistry trackingBeanRegistry) {
MockitoBeans tracker = null;
try {
tracker = (MockitoBeans) trackingBeanRegistry.getSingleton(MockitoBeans.class.getName());
}
catch (NoSuchBeanDefinitionException ignored) {
getMockitoBeans(trackingBeanRegistry).add(mock);
}

private static MockitoBeans getMockitoBeans(SingletonBeanRegistry trackingBeanRegistry) {
String beanName = MockitoBeans.class.getName();
MockitoBeans mockitoBeans = null;
if (trackingBeanRegistry.containsSingleton(beanName)) {
mockitoBeans = (MockitoBeans) trackingBeanRegistry.getSingleton(beanName);
}
if (tracker == null) {
tracker = new MockitoBeans();
trackingBeanRegistry.registerSingleton(MockitoBeans.class.getName(), tracker);
if (mockitoBeans == null) {
mockitoBeans = new MockitoBeans();
trackingBeanRegistry.registerSingleton(beanName, mockitoBeans);
}
tracker.add(mock);
return mockitoBeans;
}

@Override
public boolean equals(@Nullable Object other) {
if (other == this) {
return true;
}
return (other instanceof MockitoOverrideMetadata that && super.equals(that) &&
return (other instanceof AbstractMockitoOverrideMetadata that && super.equals(that) &&
(this.reset == that.reset) && (this.proxyTargetAware == that.proxyTargetAware));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

package org.springframework.test.context.bean.override.mockito;

import java.util.List;

import org.mockito.MockSettings;
import org.mockito.MockingDetails;
import org.mockito.Mockito;
Expand All @@ -28,11 +26,15 @@
import org.springframework.util.Assert;

/**
* Reset strategy used on a mock bean. Usually applied to a mock through the
* {@link MockitoBean @MockitoBean} annotation but can also be directly applied
* to any mock in the {@code ApplicationContext} using the static methods.
* Reset strategy used on a mock bean.
*
* <p>Usually applied to a mock via the {@link MockitoBean @MockitoBean} or
* {@link MockitoSpyBean @MockitoSpyBean} annotation but can also be directly
* applied to any mock in the {@code ApplicationContext} using the static methods
* in this class.
*
* @author Phillip Webb
* @author Sam Brannen
* @since 6.2
* @see MockitoResetTestExecutionListener
*/
Expand All @@ -49,7 +51,7 @@ public enum MockReset {
AFTER,

/**
* Don't reset the mock.
* Do not reset the mock.
*/
NONE;

Expand Down Expand Up @@ -102,39 +104,26 @@ public static MockSettings apply(MockReset reset, MockSettings settings) {
* @return the reset type (never {@code null})
*/
static MockReset get(Object mock) {
MockReset reset = MockReset.NONE;
MockingDetails mockingDetails = Mockito.mockingDetails(mock);
if (mockingDetails.isMock()) {
MockCreationSettings<?> settings = mockingDetails.getMockCreationSettings();
List<InvocationListener> listeners = settings.getInvocationListeners();
for (Object listener : listeners) {
for (InvocationListener listener : settings.getInvocationListeners()) {
if (listener instanceof ResetInvocationListener resetInvocationListener) {
reset = resetInvocationListener.getReset();
return resetInvocationListener.reset;
}
}
}
return reset;
return MockReset.NONE;
}

/**
* Dummy {@link InvocationListener} used to hold the {@link MockReset} value.
*/
private static class ResetInvocationListener implements InvocationListener {

private final MockReset reset;

ResetInvocationListener(MockReset reset) {
this.reset = reset;
}

MockReset getReset() {
return this.reset;
}
private record ResetInvocationListener(MockReset reset) implements InvocationListener {

@Override
public void reportInvocation(MethodInvocationReport methodInvocationReport) {
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@
import org.springframework.test.context.bean.override.BeanOverride;

/**
* Mark a field to trigger a bean override using a Mockito mock.
* {@code @MockitoBean} is an annotation that can be applied to a field in a test
* class to override a bean in the test's
* {@link org.springframework.context.ApplicationContext ApplicationContext}
* using a Mockito mock.
*
* <p>If no explicit {@link #name()} is specified, a target bean definition is
* selected according to the class of the annotated field, and there must be
* selected according to the type of the annotated field, and there must be
* exactly one such candidate definition in the context. A {@code @Qualifier}
* annotation can be used to help disambiguate.
* If a {@link #name()} is specified, either the definition exists in the
Expand All @@ -40,13 +43,14 @@
*
* <p>Dependencies that are known to the application context but are not beans
* (such as those
* {@link org.springframework.beans.factory.config.ConfigurableListableBeanFactory#registerResolvableDependency(Class, Object)
* {@linkplain org.springframework.beans.factory.config.ConfigurableListableBeanFactory#registerResolvableDependency(Class, Object)
* registered directly}) will not be found, and a mocked bean will be added to
* the context alongside the existing dependency.
*
* @author Simon Baslé
* @since 6.2
* @see MockitoSpyBean
* @see org.springframework.test.context.bean.override.mockito.MockitoSpyBean @MockitoSpyBean
* @see org.springframework.test.context.bean.override.convention.TestBean @TestBean
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
Expand All @@ -63,15 +67,15 @@
String name() default "";

/**
* Extra interfaces that should also be declared on the mock.
* Extra interfaces that should also be declared by the mock.
* <p>Defaults to none.
* @return any extra interfaces
* @see MockSettings#extraInterfaces(Class...)
*/
Class<?>[] extraInterfaces() default {};

/**
* The {@link Answers} type to use on the mock.
* The {@link Answers} type to use in the mock.
* <p>Defaults to {@link Answers#RETURNS_DEFAULTS}.
* @return the answer type
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
* @author Sam Brannen
* @since 6.2
*/
class MockitoBeanOverrideMetadata extends MockitoOverrideMetadata {
class MockitoBeanOverrideMetadata extends AbstractMockitoOverrideMetadata {

private final Set<Class<?>> extraInterfaces;

Expand Down
Loading

0 comments on commit 40ca83d

Please sign in to comment.