diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverride.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverride.java
index f5be7dd77757..a25dff0a1d50 100644
--- a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverride.java
+++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverride.java
@@ -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;
@@ -24,17 +25,23 @@
/**
* Mark a composed annotation as eligible for Bean Override processing.
*
- *
Specifying this annotation triggers the configured {@link BeanOverrideProcessor}
+ *
Specifying this annotation registers the configured {@link BeanOverrideProcessor}
* which must be capable of handling the composed annotation and its attributes.
*
*
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)}.
+ *
+ *
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 {
/**
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 04b13179fb29..ba38bce1b3ba 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
@@ -63,12 +63,12 @@
*/
class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered {
+ private static final BeanNameGenerator beanNameGenerator = DefaultBeanNameGenerator.INSTANCE;
+
private final Set metadata;
private final BeanOverrideRegistrar overrideRegistrar;
- private final BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();
-
/**
* Create a new {@code BeanOverrideBeanFactoryPostProcessor} with the supplied
@@ -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();
@@ -230,7 +230,7 @@ private Set 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 beanNames = new LinkedHashSet<>(
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 e6d10dcae56c..c1570024941c 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
@@ -26,7 +26,7 @@
* 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.
*
*
Implementations are required to have a no-argument constructor and be
* stateless.
@@ -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
diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideStrategy.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideStrategy.java
index 42cca189965a..72e459cb0132 100644
--- a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideStrategy.java
+++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideStrategy.java
@@ -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.
- *
Fails if the original bean definition exists. To create a new bean
- * definition in such a case, use {@link #REPLACE_OR_CREATE_DEFINITION}.
+ *
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,
diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBean.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBean.java
index 03d23ef9f808..0ff1c0f54b35 100644
--- a/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBean.java
+++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBean.java
@@ -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.
*
*
By default, the bean to override is inferred from the type of the
* annotated field. This requires that exactly one matching bean definition is
@@ -57,13 +60,12 @@
*
*
Consider the following example.
*
- *
- * class CustomerServiceTests {
+ * class CustomerServiceTests {
*
* @TestBean
* private CustomerRepository repository;
*
- * // Tests
+ * // @Test methods ...
*
* private static CustomerRepository repository() {
* return new TestCustomerRepository();
@@ -79,15 +81,14 @@
* To make things more explicit, the bean and method names can be set,
* as shown in the following example.
*
- *
- * class CustomerServiceTests {
+ * class CustomerServiceTests {
*
* @TestBean(name = "customerRepository", methodName = "createTestCustomerRepository")
- * private CustomerRepository repository;
+ * CustomerRepository repository;
*
- * // Tests
+ * // @Test methods ...
*
- * private static CustomerRepository createTestCustomerRepository() {
+ * static CustomerRepository createTestCustomerRepository() {
* return new TestCustomerRepository();
* }
* }
@@ -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)
diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoOverrideMetadata.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/AbstractMockitoOverrideMetadata.java
similarity index 74%
rename from spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoOverrideMetadata.java
rename to spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/AbstractMockitoOverrideMetadata.java
index 2ca61fc5ed40..6c31e4fae212 100644
--- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoOverrideMetadata.java
+++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/AbstractMockitoOverrideMetadata.java
@@ -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;
@@ -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);
@@ -69,17 +68,20 @@ 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
@@ -87,7 +89,7 @@ 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));
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockReset.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockReset.java
index d8d8cbed0ffc..3a12bc92ff01 100644
--- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockReset.java
+++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockReset.java
@@ -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;
@@ -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.
+ *
+ * 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
*/
@@ -49,7 +51,7 @@ public enum MockReset {
AFTER,
/**
- * Don't reset the mock.
+ * Do not reset the mock.
*/
NONE;
@@ -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 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) {
}
-
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBean.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBean.java
index d865e559a6cc..9d0737392fb3 100644
--- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBean.java
+++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBean.java
@@ -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.
*
* 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
@@ -40,13 +43,14 @@
*
*
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)
@@ -63,7 +67,7 @@
String name() default "";
/**
- * Extra interfaces that should also be declared on the mock.
+ * Extra interfaces that should also be declared by the mock.
*
Defaults to none.
* @return any extra interfaces
* @see MockSettings#extraInterfaces(Class...)
@@ -71,7 +75,7 @@
Class>[] extraInterfaces() default {};
/**
- * The {@link Answers} type to use on the mock.
+ * The {@link Answers} type to use in the mock.
*
Defaults to {@link Answers#RETURNS_DEFAULTS}.
* @return the answer type
*/
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/MockitoBeanOverrideMetadata.java
index cbb4505427b9..765c114d89a1 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/MockitoBeanOverrideMetadata.java
@@ -45,7 +45,7 @@
* @author Sam Brannen
* @since 6.2
*/
-class MockitoBeanOverrideMetadata extends MockitoOverrideMetadata {
+class MockitoBeanOverrideMetadata extends AbstractMockitoOverrideMetadata {
private final Set> 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 febb7538b3bc..16c16411ba9b 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
@@ -23,27 +23,28 @@
import org.springframework.test.context.bean.override.BeanOverrideProcessor;
/**
- * {@link BeanOverrideProcessor} implementation for Mockito support. Both mocking
- * and spying are supported.
+ * {@link BeanOverrideProcessor} implementation that provides support for
+ * {@link MockitoBean @MockitoBean} and {@link MockitoSpyBean @MockitoSpyBean}.
*
* @author Simon Baslé
* @since 6.2
- * @see MockitoBean
- * @see MockitoSpyBean
+ * @see MockitoBean @MockitoBean
+ * @see MockitoSpyBean @MockitoSpyBean
*/
class MockitoBeanOverrideProcessor implements BeanOverrideProcessor {
@Override
- public MockitoOverrideMetadata createMetadata(Annotation overrideAnnotation, Class> testClass, Field field) {
+ public AbstractMockitoOverrideMetadata createMetadata(Annotation overrideAnnotation, Class> testClass, Field field) {
if (overrideAnnotation instanceof MockitoBean mockBean) {
return new MockitoBeanOverrideMetadata(field, ResolvableType.forField(field, testClass), mockBean);
}
else if (overrideAnnotation instanceof MockitoSpyBean spyBean) {
return new MockitoSpyBeanOverrideMetadata(field, ResolvableType.forField(field, testClass), spyBean);
}
- throw new IllegalStateException(String.format("Invalid annotation passed to MockitoBeanOverrideProcessor: "
- + "expected @MockitoBean/@MockitoSpyBean on field %s.%s",
- field.getDeclaringClass().getName(), field.getName()));
+ throw new IllegalStateException("""
+ Invalid annotation passed to MockitoBeanOverrideProcessor: \
+ expected either @MockitoBean or @MockitoSpyBean on field %s.%s"""
+ .formatted(field.getDeclaringClass().getName(), field.getName()));
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanSettings.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanSettings.java
index 33f0f5f8a253..c375af2b653a 100644
--- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanSettings.java
+++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanSettings.java
@@ -25,8 +25,9 @@
import org.mockito.quality.Strictness;
/**
- * Configure a test class that uses {@link MockitoBean} or {@link MockitoSpyBean}
- * to set up Mockito with an explicitly specified stubbing strictness.
+ * Configure a test class that uses {@link MockitoBean @MockitoBean} or
+ * {@link MockitoSpyBean @MockitoSpyBean} to set up Mockito with an explicit
+ * stubbing strictness mode.
*
* @author Simon Baslé
* @since 6.2
@@ -38,8 +39,8 @@
public @interface MockitoBeanSettings {
/**
- * The stubbing strictness to apply for all Mockito mocks in the annotated
- * class.
+ * The stubbing strictness mode to apply for all Mockito mocks in the annotated
+ * test class.
*/
Strictness value();
diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoResetTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoResetTestExecutionListener.java
index bbf8f8182033..46cf654c1858 100644
--- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoResetTestExecutionListener.java
+++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoResetTestExecutionListener.java
@@ -40,8 +40,11 @@
* with a {@link MockReset}.
*
* @author Phillip Webb
+ * @author Sam Brannen
* @since 6.2
* @see MockitoTestExecutionListener
+ * @see MockitoBean @MockitoBean
+ * @see MockitoSpyBean @MockitoSpyBean
*/
public class MockitoResetTestExecutionListener extends AbstractTestExecutionListener {
@@ -75,13 +78,13 @@ private void resetMocks(ApplicationContext applicationContext, MockReset reset)
private void resetMocks(ConfigurableApplicationContext applicationContext, MockReset reset) {
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
- String[] names = beanFactory.getBeanDefinitionNames();
+ String[] beanNames = beanFactory.getBeanDefinitionNames();
Set instantiatedSingletons = new HashSet<>(Arrays.asList(beanFactory.getSingletonNames()));
- for (String name : names) {
- BeanDefinition definition = beanFactory.getBeanDefinition(name);
- if (definition.isSingleton() && instantiatedSingletons.contains(name)) {
- Object bean = getBean(beanFactory, name);
- if (bean != null && reset.equals(MockReset.get(bean))) {
+ for (String beanName : beanNames) {
+ BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
+ if (beanDefinition.isSingleton() && instantiatedSingletons.contains(beanName)) {
+ Object bean = getBean(beanFactory, beanName);
+ if (bean != null && reset == MockReset.get(bean)) {
Mockito.reset(bean);
}
}
@@ -98,20 +101,20 @@ private void resetMocks(ConfigurableApplicationContext applicationContext, MockR
}
@Nullable
- private Object getBean(ConfigurableListableBeanFactory beanFactory, String name) {
+ private Object getBean(ConfigurableListableBeanFactory beanFactory, String beanName) {
try {
- if (isStandardBeanOrSingletonFactoryBean(beanFactory, name)) {
- return beanFactory.getBean(name);
+ if (isStandardBeanOrSingletonFactoryBean(beanFactory, beanName)) {
+ return beanFactory.getBean(beanName);
}
}
catch (Exception ex) {
// Continue
}
- return beanFactory.getSingleton(name);
+ return beanFactory.getSingleton(beanName);
}
- private boolean isStandardBeanOrSingletonFactoryBean(ConfigurableListableBeanFactory beanFactory, String name) {
- String factoryBeanName = BeanFactory.FACTORY_BEAN_PREFIX + name;
+ private boolean isStandardBeanOrSingletonFactoryBean(ConfigurableListableBeanFactory beanFactory, String beanName) {
+ String factoryBeanName = BeanFactory.FACTORY_BEAN_PREFIX + beanName;
if (beanFactory.containsBean(factoryBeanName)) {
FactoryBean> factoryBean = (FactoryBean>) beanFactory.getBean(factoryBeanName);
return factoryBean.isSingleton();
diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBean.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBean.java
index 1192cec86685..db2b8fb3ce45 100644
--- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBean.java
+++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBean.java
@@ -44,7 +44,8 @@
*
* @author Simon Baslé
* @since 6.2
- * @see MockitoBean
+ * @see org.springframework.test.context.bean.override.mockito.MockitoBean @MockitoBean
+ * @see org.springframework.test.context.bean.override.convention.TestBean @TestBean
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
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/MockitoSpyBeanOverrideMetadata.java
index 4a96d585743c..ed6234c8f8a9 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/MockitoSpyBeanOverrideMetadata.java
@@ -42,7 +42,7 @@
* @author Stephane Nicoll
* @since 6.2
*/
-class MockitoSpyBeanOverrideMetadata extends MockitoOverrideMetadata {
+class MockitoSpyBeanOverrideMetadata extends AbstractMockitoOverrideMetadata {
MockitoSpyBeanOverrideMetadata(Field field, ResolvableType typeToSpy, MockitoSpyBean spyAnnotation) {
this(field, typeToSpy, (StringUtils.hasText(spyAnnotation.name()) ? spyAnnotation.name() : null),
@@ -62,17 +62,17 @@ protected Object createOverride(String beanName, @Nullable BeanDefinition existi
@Nullable Object existingBeanInstance) {
Assert.notNull(existingBeanInstance,
- () -> "MockitoSpyBean requires an existing bean instance for bean " + beanName);
+ () -> "@MockitoSpyBean requires an existing bean instance for bean " + beanName);
return createSpy(beanName, existingBeanInstance);
}
@SuppressWarnings("unchecked")
- private T createSpy(String name, Object instance) {
+ private Object createSpy(String name, Object instance) {
Class> resolvedTypeToOverride = getBeanType().resolve();
Assert.notNull(resolvedTypeToOverride, "Failed to resolve type to override");
Assert.isInstanceOf(resolvedTypeToOverride, instance);
if (Mockito.mockingDetails(instance).isSpy()) {
- return (T) instance;
+ return instance;
}
MockSettings settings = MockReset.withSettings(getReset());
if (StringUtils.hasLength(name)) {
@@ -91,7 +91,7 @@ private T createSpy(String name, Object instance) {
settings.spiedInstance(instance);
toSpy = instance.getClass();
}
- return (T) Mockito.mock(toSpy, settings);
+ return Mockito.mock(toSpy, settings);
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoTestExecutionListener.java
index 3eca29fad200..aca1a6aaff11 100644
--- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoTestExecutionListener.java
+++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoTestExecutionListener.java
@@ -17,9 +17,11 @@
package org.springframework.test.context.bean.override.mockito;
import java.lang.annotation.Annotation;
-import java.lang.reflect.Field;
-import java.util.LinkedHashSet;
+import java.lang.reflect.AnnotatedElement;
+import java.util.Arrays;
+import java.util.HashSet;
import java.util.Set;
+import java.util.function.Predicate;
import org.mockito.Mockito;
import org.mockito.MockitoSession;
@@ -31,7 +33,6 @@
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
-import org.springframework.util.ReflectionUtils.FieldCallback;
/**
* {@code TestExecutionListener} that enables {@link MockitoBean @MockitoBean}
@@ -41,7 +42,7 @@
*
* The {@link MockitoSession#setStrictness(Strictness) strictness} of the
* session defaults to {@link Strictness#STRICT_STUBS}. Use
- * {@link MockitoBeanSettings} to specify a different strictness.
+ * {@link MockitoBeanSettings @MockitoBeanSettings} to specify a different strictness.
*
*
The automatic reset support for {@code @MockBean} and {@code @SpyBean} is
* handled by the {@link MockitoResetTestExecutionListener}.
@@ -50,8 +51,11 @@
* @author Phillip Webb
* @author Andy Wilkinson
* @author Moritz Halbritter
+ * @author Sam Brannen
* @since 6.2
* @see MockitoResetTestExecutionListener
+ * @see MockitoBean @MockitoBean
+ * @see MockitoSpyBean @MockitoSpyBean
*/
public class MockitoTestExecutionListener extends AbstractTestExecutionListener {
@@ -101,7 +105,7 @@ public void afterTestClass(TestContext testContext) throws Exception {
}
private void initMocks(TestContext testContext) {
- if (hasMockitoAnnotations(testContext)) {
+ if (MockitoAnnotationDetector.hasMockitoAnnotations(testContext.getTestClass())) {
Object testInstance = testContext.getTestInstance();
MockitoBeanSettings annotation = AnnotationUtils.findAnnotation(testInstance.getClass(),
MockitoBeanSettings.class);
@@ -124,51 +128,35 @@ private void closeMocks(TestContext testContext) throws Exception {
}
}
- private boolean hasMockitoAnnotations(TestContext testContext) {
- MockitoAnnotationCollector collector = new MockitoAnnotationCollector();
- collector.collect(testContext.getTestClass());
- return collector.hasAnnotations();
- }
-
/**
- * Utility class that collects {@code org.mockito} annotations and the
- * annotations in this package (like {@link MockitoBeanSettings}).
+ * Utility class that detects {@code org.mockito} annotations as well as the
+ * annotations in this package (like {@link MockitoBeanSettings @MockitoBeanSettings}).
*/
- private static final class MockitoAnnotationCollector implements FieldCallback {
+ private static class MockitoAnnotationDetector {
- private static final String MOCKITO_BEAN_PACKAGE = MockitoBean.class.getPackageName();
+ private static final String MOCKITO_BEAN_PACKAGE = MockitoBeanSettings.class.getPackageName();
private static final String ORG_MOCKITO_PACKAGE = "org.mockito";
- private final Set annotations = new LinkedHashSet<>();
-
- public void collect(Class> clazz) {
- ReflectionUtils.doWithFields(clazz, this);
- for (Annotation annotation : clazz.getAnnotations()) {
- collect(annotation);
- }
- }
-
- @Override
- public void doWith(Field field) throws IllegalArgumentException {
- for (Annotation annotation : field.getAnnotations()) {
- collect(annotation);
- }
- }
-
- private void collect(Annotation annotation) {
- String packageName = annotation.annotationType().getPackageName();
- if (packageName.startsWith(MOCKITO_BEAN_PACKAGE) ||
- packageName.startsWith(ORG_MOCKITO_PACKAGE)) {
- this.annotations.add(annotation);
- }
+ private static final Predicate isMockitoAnnotation = annotation -> {
+ String packageName = annotation.annotationType().getPackageName();
+ return (packageName.startsWith(MOCKITO_BEAN_PACKAGE) ||
+ packageName.startsWith(ORG_MOCKITO_PACKAGE));
+ };
+
+ static boolean hasMockitoAnnotations(Class> testClass) {
+ Set annotations = new HashSet<>();
+ collect(testClass, annotations);
+ ReflectionUtils.doWithFields(testClass, field -> collect(field, annotations));
+ return !annotations.isEmpty();
}
- boolean hasAnnotations() {
- return !this.annotations.isEmpty();
+ static void collect(AnnotatedElement annotatedElement, Set annotations) {
+ Arrays.stream(annotatedElement.getAnnotations())
+ .filter(isMockitoAnnotation)
+ .forEach(annotations::add);
}
-
}
}
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/EasyMockBeanOverrideMetadata.java
index accbcb6aa8fc..96b9e3c13d77 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/EasyMockBeanOverrideMetadata.java
@@ -21,7 +21,6 @@
import org.easymock.EasyMock;
import org.easymock.MockType;
-import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.core.ResolvableType;
@@ -62,17 +61,15 @@ protected void track(Object mock, SingletonBeanRegistry singletonBeanRegistry) {
getEasyMockBeans(singletonBeanRegistry).add(mock);
}
- private EasyMockBeans getEasyMockBeans(SingletonBeanRegistry singletonBeanRegistry) {
- String className = EasyMockBeans.class.getName();
+ private static EasyMockBeans getEasyMockBeans(SingletonBeanRegistry singletonBeanRegistry) {
+ String beanName = EasyMockBeans.class.getName();
EasyMockBeans easyMockBeans = null;
- try {
- easyMockBeans = (EasyMockBeans) singletonBeanRegistry.getSingleton(className);
- }
- catch (NoSuchBeanDefinitionException ignored) {
+ if (singletonBeanRegistry.containsSingleton(beanName)) {
+ easyMockBeans = (EasyMockBeans) singletonBeanRegistry.getSingleton(beanName);
}
if (easyMockBeans == null) {
easyMockBeans = new EasyMockBeans();
- singletonBeanRegistry.registerSingleton(className, easyMockBeans);
+ singletonBeanRegistry.registerSingleton(beanName, easyMockBeans);
}
return easyMockBeans;
}
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 e6a7a09f408a..954c77d0e9b2 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
@@ -34,7 +34,7 @@ class EasyMockBeanOverrideProcessor implements BeanOverrideProcessor {
@Override
public OverrideMetadata createMetadata(Annotation annotation, Class> testClass, Field field) {
EasyMockBean easyMockBean = (EasyMockBean) annotation;
- String beanName = (StringUtils.hasText(easyMockBean.name()) ? easyMockBean.name() : field.getName());
+ String beanName = (StringUtils.hasText(easyMockBean.name()) ? easyMockBean.name() : null);
return new EasyMockBeanOverrideMetadata(field, field.getType(), beanName, easyMockBean.mockType());
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/easymock/EasyMockResetTestExecutionListener.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/easymock/EasyMockResetTestExecutionListener.java
index bb5d46bfbb50..723f9ae04d60 100644
--- a/spring-test/src/test/java/org/springframework/test/context/bean/override/easymock/EasyMockResetTestExecutionListener.java
+++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/easymock/EasyMockResetTestExecutionListener.java
@@ -16,6 +16,7 @@
package org.springframework.test.context.bean.override.easymock;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
@@ -55,7 +56,15 @@ private void resetMocks(ApplicationContext applicationContext) {
private void resetMocks(ConfigurableApplicationContext applicationContext) {
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
- beanFactory.getBean(EasyMockBeans.class).resetAll();
+ try {
+ beanFactory.getBean(EasyMockBeans.class).resetAll();
+ }
+ catch (NoSuchBeanDefinitionException ex) {
+ // Continue
+ }
+ if (applicationContext.getParent() != null) {
+ resetMocks(applicationContext.getParent());
+ }
}
}
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 eed7c1a8443d..19feda9f2f65 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
@@ -64,8 +64,8 @@ void otherAnnotationThrows() throws NoSuchFieldException {
assertThatIllegalStateException()
.isThrownBy(() -> this.processor.createMetadata(annotation, clazz, field))
- .withMessage("Invalid annotation passed to MockitoBeanOverrideProcessor: expected " +
- "@MockitoBean/@MockitoSpyBean on field %s.%s", field.getDeclaringClass().getName(),
+ .withMessage("Invalid annotation passed to MockitoBeanOverrideProcessor: expected either " +
+ "@MockitoBean or @MockitoSpyBean on field %s.%s", field.getDeclaringClass().getName(),
field.getName());
}