Revise Bean Override internals and Javadoc
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.test.context.bean.override;
|
package org.springframework.test.context.bean.override;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
@@ -24,17 +25,23 @@ import java.lang.annotation.Target;
|
|||||||
/**
|
/**
|
||||||
* Mark a composed annotation as eligible for Bean Override processing.
|
* 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.
|
* 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
|
* <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é
|
* @author Simon Baslé
|
||||||
* @since 6.2
|
* @since 6.2
|
||||||
*/
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.ANNOTATION_TYPE)
|
@Target(ElementType.ANNOTATION_TYPE)
|
||||||
|
@Documented
|
||||||
public @interface BeanOverride {
|
public @interface BeanOverride {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -63,12 +63,12 @@ import org.springframework.util.StringUtils;
|
|||||||
*/
|
*/
|
||||||
class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered {
|
class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered {
|
||||||
|
|
||||||
|
private static final BeanNameGenerator beanNameGenerator = DefaultBeanNameGenerator.INSTANCE;
|
||||||
|
|
||||||
private final Set<OverrideMetadata> metadata;
|
private final Set<OverrideMetadata> metadata;
|
||||||
|
|
||||||
private final BeanOverrideRegistrar overrideRegistrar;
|
private final BeanOverrideRegistrar overrideRegistrar;
|
||||||
|
|
||||||
private final BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@code BeanOverrideBeanFactoryPostProcessor} with the supplied
|
* Create a new {@code BeanOverrideBeanFactoryPostProcessor} with the supplied
|
||||||
@@ -215,7 +215,7 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
|
|||||||
"Unable to override bean: no bean definitions of type %s (as required by annotated field '%s.%s')"
|
"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()));
|
.formatted(overrideMetadata.getBeanType(), field.getDeclaringClass().getSimpleName(), field.getName()));
|
||||||
}
|
}
|
||||||
return this.beanNameGenerator.generateBeanName(beanDefinition, registry);
|
return beanNameGenerator.generateBeanName(beanDefinition, registry);
|
||||||
}
|
}
|
||||||
|
|
||||||
Field field = overrideMetadata.getField();
|
Field field = overrideMetadata.getField();
|
||||||
@@ -230,7 +230,7 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
|
|||||||
boolean checkAutowiredCandidate) {
|
boolean checkAutowiredCandidate) {
|
||||||
|
|
||||||
ResolvableType resolvableType = metadata.getBeanType();
|
ResolvableType resolvableType = metadata.getBeanType();
|
||||||
Class<?> type = resolvableType.resolve(Object.class);
|
Class<?> type = resolvableType.toClass();
|
||||||
|
|
||||||
// Start with matching bean names for type, excluding FactoryBeans.
|
// Start with matching bean names for type, excluding FactoryBeans.
|
||||||
Set<String> beanNames = new LinkedHashSet<>(
|
Set<String> beanNames = new LinkedHashSet<>(
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import java.lang.reflect.Field;
|
|||||||
* <p>At least one composed annotation that is meta-annotated with
|
* <p>At least one composed annotation that is meta-annotated with
|
||||||
* {@link BeanOverride @BeanOverride} must be a companion of this processor and
|
* {@link BeanOverride @BeanOverride} must be a companion of this processor and
|
||||||
* may provide additional user settings that drive how the concrete
|
* 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
|
* <p>Implementations are required to have a no-argument constructor and be
|
||||||
* stateless.
|
* stateless.
|
||||||
@@ -41,7 +41,7 @@ public interface BeanOverrideProcessor {
|
|||||||
/**
|
/**
|
||||||
* Create an {@link OverrideMetadata} instance for the given annotated field.
|
* Create an {@link OverrideMetadata} instance for the given annotated field.
|
||||||
* @param overrideAnnotation the composed annotation that declares the
|
* @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 testClass the test class to process
|
||||||
* @param field the annotated field
|
* @param field the annotated field
|
||||||
* @return the {@link OverrideMetadata} instance that should handle the
|
* @return the {@link OverrideMetadata} instance that should handle the
|
||||||
|
|||||||
@@ -21,14 +21,15 @@ package org.springframework.test.context.bean.override;
|
|||||||
*
|
*
|
||||||
* @author Simon Baslé
|
* @author Simon Baslé
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author Sam Brannen
|
||||||
* @since 6.2
|
* @since 6.2
|
||||||
*/
|
*/
|
||||||
public enum BeanOverrideStrategy {
|
public enum BeanOverrideStrategy {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace a given bean definition, immediately preparing a singleton instance.
|
* Replace a given bean definition, immediately preparing a singleton instance.
|
||||||
* <p>Fails if the original bean definition exists. To create a new bean
|
* <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}.
|
* definition in such a case, use {@link #REPLACE_OR_CREATE_DEFINITION} instead.
|
||||||
*/
|
*/
|
||||||
REPLACE_DEFINITION,
|
REPLACE_DEFINITION,
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,10 @@ import org.springframework.core.annotation.AliasFor;
|
|||||||
import org.springframework.test.context.bean.override.BeanOverride;
|
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
|
* <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
|
* annotated field. This requires that exactly one matching bean definition is
|
||||||
@@ -57,13 +60,12 @@ import org.springframework.test.context.bean.override.BeanOverride;
|
|||||||
*
|
*
|
||||||
* <p>Consider the following example.
|
* <p>Consider the following example.
|
||||||
*
|
*
|
||||||
* <pre><code>
|
* <pre><code> class CustomerServiceTests {
|
||||||
* class CustomerServiceTests {
|
|
||||||
*
|
*
|
||||||
* @TestBean
|
* @TestBean
|
||||||
* private CustomerRepository repository;
|
* private CustomerRepository repository;
|
||||||
*
|
*
|
||||||
* // Tests
|
* // @Test methods ...
|
||||||
*
|
*
|
||||||
* private static CustomerRepository repository() {
|
* private static CustomerRepository repository() {
|
||||||
* return new TestCustomerRepository();
|
* return new TestCustomerRepository();
|
||||||
@@ -79,15 +81,14 @@ import org.springframework.test.context.bean.override.BeanOverride;
|
|||||||
* <p>To make things more explicit, the bean and method names can be set,
|
* <p>To make things more explicit, the bean and method names can be set,
|
||||||
* as shown in the following example.
|
* as shown in the following example.
|
||||||
*
|
*
|
||||||
* <pre><code>
|
* <pre><code> class CustomerServiceTests {
|
||||||
* class CustomerServiceTests {
|
|
||||||
*
|
*
|
||||||
* @TestBean(name = "customerRepository", methodName = "createTestCustomerRepository")
|
* @TestBean(name = "customerRepository", methodName = "createTestCustomerRepository")
|
||||||
* private CustomerRepository repository;
|
* CustomerRepository repository;
|
||||||
*
|
*
|
||||||
* // Tests
|
* // @Test methods ...
|
||||||
*
|
*
|
||||||
* private static CustomerRepository createTestCustomerRepository() {
|
* static CustomerRepository createTestCustomerRepository() {
|
||||||
* return new TestCustomerRepository();
|
* return new TestCustomerRepository();
|
||||||
* }
|
* }
|
||||||
* }</code></pre>
|
* }</code></pre>
|
||||||
@@ -96,7 +97,8 @@ import org.springframework.test.context.bean.override.BeanOverride;
|
|||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 6.2
|
* @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)
|
@Target(ElementType.FIELD)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package org.springframework.test.context.bean.override.mockito;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|
||||||
import org.springframework.beans.factory.config.SingletonBeanRegistry;
|
import org.springframework.beans.factory.config.SingletonBeanRegistry;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.style.ToStringCreator;
|
import org.springframework.core.style.ToStringCreator;
|
||||||
@@ -28,21 +27,21 @@ import org.springframework.test.context.bean.override.BeanOverrideStrategy;
|
|||||||
import org.springframework.test.context.bean.override.OverrideMetadata;
|
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 Phillip Webb
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 6.2
|
* @since 6.2
|
||||||
*/
|
*/
|
||||||
abstract class MockitoOverrideMetadata extends OverrideMetadata {
|
abstract class AbstractMockitoOverrideMetadata extends OverrideMetadata {
|
||||||
|
|
||||||
private final MockReset reset;
|
private final MockReset reset;
|
||||||
|
|
||||||
private final boolean proxyTargetAware;
|
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) {
|
BeanOverrideStrategy strategy, @Nullable MockReset reset, boolean proxyTargetAware) {
|
||||||
|
|
||||||
super(field, beanType, beanName, strategy);
|
super(field, beanType, beanName, strategy);
|
||||||
@@ -69,17 +68,20 @@ abstract class MockitoOverrideMetadata extends OverrideMetadata {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void track(Object mock, SingletonBeanRegistry trackingBeanRegistry) {
|
protected void track(Object mock, SingletonBeanRegistry trackingBeanRegistry) {
|
||||||
MockitoBeans tracker = null;
|
getMockitoBeans(trackingBeanRegistry).add(mock);
|
||||||
try {
|
}
|
||||||
tracker = (MockitoBeans) trackingBeanRegistry.getSingleton(MockitoBeans.class.getName());
|
|
||||||
|
private static MockitoBeans getMockitoBeans(SingletonBeanRegistry trackingBeanRegistry) {
|
||||||
|
String beanName = MockitoBeans.class.getName();
|
||||||
|
MockitoBeans mockitoBeans = null;
|
||||||
|
if (trackingBeanRegistry.containsSingleton(beanName)) {
|
||||||
|
mockitoBeans = (MockitoBeans) trackingBeanRegistry.getSingleton(beanName);
|
||||||
}
|
}
|
||||||
catch (NoSuchBeanDefinitionException ignored) {
|
if (mockitoBeans == null) {
|
||||||
|
mockitoBeans = new MockitoBeans();
|
||||||
|
trackingBeanRegistry.registerSingleton(beanName, mockitoBeans);
|
||||||
}
|
}
|
||||||
if (tracker == null) {
|
return mockitoBeans;
|
||||||
tracker = new MockitoBeans();
|
|
||||||
trackingBeanRegistry.registerSingleton(MockitoBeans.class.getName(), tracker);
|
|
||||||
}
|
|
||||||
tracker.add(mock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -87,7 +89,7 @@ abstract class MockitoOverrideMetadata extends OverrideMetadata {
|
|||||||
if (other == this) {
|
if (other == this) {
|
||||||
return true;
|
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));
|
(this.reset == that.reset) && (this.proxyTargetAware == that.proxyTargetAware));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.test.context.bean.override.mockito;
|
package org.springframework.test.context.bean.override.mockito;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.mockito.MockSettings;
|
import org.mockito.MockSettings;
|
||||||
import org.mockito.MockingDetails;
|
import org.mockito.MockingDetails;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
@@ -28,11 +26,15 @@ import org.mockito.mock.MockCreationSettings;
|
|||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset strategy used on a mock bean. Usually applied to a mock through the
|
* Reset strategy used on a mock bean.
|
||||||
* {@link MockitoBean @MockitoBean} annotation but can also be directly applied
|
*
|
||||||
* to any mock in the {@code ApplicationContext} using the static methods.
|
* <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 Phillip Webb
|
||||||
|
* @author Sam Brannen
|
||||||
* @since 6.2
|
* @since 6.2
|
||||||
* @see MockitoResetTestExecutionListener
|
* @see MockitoResetTestExecutionListener
|
||||||
*/
|
*/
|
||||||
@@ -49,7 +51,7 @@ public enum MockReset {
|
|||||||
AFTER,
|
AFTER,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Don't reset the mock.
|
* Do not reset the mock.
|
||||||
*/
|
*/
|
||||||
NONE;
|
NONE;
|
||||||
|
|
||||||
@@ -102,39 +104,26 @@ public enum MockReset {
|
|||||||
* @return the reset type (never {@code null})
|
* @return the reset type (never {@code null})
|
||||||
*/
|
*/
|
||||||
static MockReset get(Object mock) {
|
static MockReset get(Object mock) {
|
||||||
MockReset reset = MockReset.NONE;
|
|
||||||
MockingDetails mockingDetails = Mockito.mockingDetails(mock);
|
MockingDetails mockingDetails = Mockito.mockingDetails(mock);
|
||||||
if (mockingDetails.isMock()) {
|
if (mockingDetails.isMock()) {
|
||||||
MockCreationSettings<?> settings = mockingDetails.getMockCreationSettings();
|
MockCreationSettings<?> settings = mockingDetails.getMockCreationSettings();
|
||||||
List<InvocationListener> listeners = settings.getInvocationListeners();
|
for (InvocationListener listener : settings.getInvocationListeners()) {
|
||||||
for (Object listener : listeners) {
|
|
||||||
if (listener instanceof ResetInvocationListener resetInvocationListener) {
|
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.
|
* Dummy {@link InvocationListener} used to hold the {@link MockReset} value.
|
||||||
*/
|
*/
|
||||||
private static class ResetInvocationListener implements InvocationListener {
|
private record ResetInvocationListener(MockReset reset) implements InvocationListener {
|
||||||
|
|
||||||
private final MockReset reset;
|
|
||||||
|
|
||||||
ResetInvocationListener(MockReset reset) {
|
|
||||||
this.reset = reset;
|
|
||||||
}
|
|
||||||
|
|
||||||
MockReset getReset() {
|
|
||||||
return this.reset;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reportInvocation(MethodInvocationReport methodInvocationReport) {
|
public void reportInvocation(MethodInvocationReport methodInvocationReport) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,10 +28,13 @@ import org.mockito.MockSettings;
|
|||||||
import org.springframework.test.context.bean.override.BeanOverride;
|
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
|
* <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}
|
* exactly one such candidate definition in the context. A {@code @Qualifier}
|
||||||
* annotation can be used to help disambiguate.
|
* annotation can be used to help disambiguate.
|
||||||
* If a {@link #name()} is specified, either the definition exists in the
|
* If a {@link #name()} is specified, either the definition exists in the
|
||||||
@@ -40,13 +43,14 @@ import org.springframework.test.context.bean.override.BeanOverride;
|
|||||||
*
|
*
|
||||||
* <p>Dependencies that are known to the application context but are not beans
|
* <p>Dependencies that are known to the application context but are not beans
|
||||||
* (such as those
|
* (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
|
* registered directly}) will not be found, and a mocked bean will be added to
|
||||||
* the context alongside the existing dependency.
|
* the context alongside the existing dependency.
|
||||||
*
|
*
|
||||||
* @author Simon Baslé
|
* @author Simon Baslé
|
||||||
* @since 6.2
|
* @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)
|
@Target(ElementType.FIELD)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@@ -63,7 +67,7 @@ public @interface MockitoBean {
|
|||||||
String name() default "";
|
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.
|
* <p>Defaults to none.
|
||||||
* @return any extra interfaces
|
* @return any extra interfaces
|
||||||
* @see MockSettings#extraInterfaces(Class...)
|
* @see MockSettings#extraInterfaces(Class...)
|
||||||
@@ -71,7 +75,7 @@ public @interface MockitoBean {
|
|||||||
Class<?>[] extraInterfaces() default {};
|
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}.
|
* <p>Defaults to {@link Answers#RETURNS_DEFAULTS}.
|
||||||
* @return the answer type
|
* @return the answer type
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ import org.springframework.util.StringUtils;
|
|||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 6.2
|
* @since 6.2
|
||||||
*/
|
*/
|
||||||
class MockitoBeanOverrideMetadata extends MockitoOverrideMetadata {
|
class MockitoBeanOverrideMetadata extends AbstractMockitoOverrideMetadata {
|
||||||
|
|
||||||
private final Set<Class<?>> extraInterfaces;
|
private final Set<Class<?>> extraInterfaces;
|
||||||
|
|
||||||
|
|||||||
@@ -23,27 +23,28 @@ import org.springframework.core.ResolvableType;
|
|||||||
import org.springframework.test.context.bean.override.BeanOverrideProcessor;
|
import org.springframework.test.context.bean.override.BeanOverrideProcessor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link BeanOverrideProcessor} implementation for Mockito support. Both mocking
|
* {@link BeanOverrideProcessor} implementation that provides support for
|
||||||
* and spying are supported.
|
* {@link MockitoBean @MockitoBean} and {@link MockitoSpyBean @MockitoSpyBean}.
|
||||||
*
|
*
|
||||||
* @author Simon Baslé
|
* @author Simon Baslé
|
||||||
* @since 6.2
|
* @since 6.2
|
||||||
* @see MockitoBean
|
* @see MockitoBean @MockitoBean
|
||||||
* @see MockitoSpyBean
|
* @see MockitoSpyBean @MockitoSpyBean
|
||||||
*/
|
*/
|
||||||
class MockitoBeanOverrideProcessor implements BeanOverrideProcessor {
|
class MockitoBeanOverrideProcessor implements BeanOverrideProcessor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MockitoOverrideMetadata createMetadata(Annotation overrideAnnotation, Class<?> testClass, Field field) {
|
public AbstractMockitoOverrideMetadata createMetadata(Annotation overrideAnnotation, Class<?> testClass, Field field) {
|
||||||
if (overrideAnnotation instanceof MockitoBean mockBean) {
|
if (overrideAnnotation instanceof MockitoBean mockBean) {
|
||||||
return new MockitoBeanOverrideMetadata(field, ResolvableType.forField(field, testClass), mockBean);
|
return new MockitoBeanOverrideMetadata(field, ResolvableType.forField(field, testClass), mockBean);
|
||||||
}
|
}
|
||||||
else if (overrideAnnotation instanceof MockitoSpyBean spyBean) {
|
else if (overrideAnnotation instanceof MockitoSpyBean spyBean) {
|
||||||
return new MockitoSpyBeanOverrideMetadata(field, ResolvableType.forField(field, testClass), spyBean);
|
return new MockitoSpyBeanOverrideMetadata(field, ResolvableType.forField(field, testClass), spyBean);
|
||||||
}
|
}
|
||||||
throw new IllegalStateException(String.format("Invalid annotation passed to MockitoBeanOverrideProcessor: "
|
throw new IllegalStateException("""
|
||||||
+ "expected @MockitoBean/@MockitoSpyBean on field %s.%s",
|
Invalid annotation passed to MockitoBeanOverrideProcessor: \
|
||||||
field.getDeclaringClass().getName(), field.getName()));
|
expected either @MockitoBean or @MockitoSpyBean on field %s.%s"""
|
||||||
|
.formatted(field.getDeclaringClass().getName(), field.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ import java.lang.annotation.Target;
|
|||||||
import org.mockito.quality.Strictness;
|
import org.mockito.quality.Strictness;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure a test class that uses {@link MockitoBean} or {@link MockitoSpyBean}
|
* Configure a test class that uses {@link MockitoBean @MockitoBean} or
|
||||||
* to set up Mockito with an explicitly specified stubbing strictness.
|
* {@link MockitoSpyBean @MockitoSpyBean} to set up Mockito with an explicit
|
||||||
|
* stubbing strictness mode.
|
||||||
*
|
*
|
||||||
* @author Simon Baslé
|
* @author Simon Baslé
|
||||||
* @since 6.2
|
* @since 6.2
|
||||||
@@ -38,8 +39,8 @@ import org.mockito.quality.Strictness;
|
|||||||
public @interface MockitoBeanSettings {
|
public @interface MockitoBeanSettings {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The stubbing strictness to apply for all Mockito mocks in the annotated
|
* The stubbing strictness mode to apply for all Mockito mocks in the annotated
|
||||||
* class.
|
* test class.
|
||||||
*/
|
*/
|
||||||
Strictness value();
|
Strictness value();
|
||||||
|
|
||||||
|
|||||||
@@ -40,8 +40,11 @@ import org.springframework.test.context.support.AbstractTestExecutionListener;
|
|||||||
* with a {@link MockReset}.
|
* with a {@link MockReset}.
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
|
* @author Sam Brannen
|
||||||
* @since 6.2
|
* @since 6.2
|
||||||
* @see MockitoTestExecutionListener
|
* @see MockitoTestExecutionListener
|
||||||
|
* @see MockitoBean @MockitoBean
|
||||||
|
* @see MockitoSpyBean @MockitoSpyBean
|
||||||
*/
|
*/
|
||||||
public class MockitoResetTestExecutionListener extends AbstractTestExecutionListener {
|
public class MockitoResetTestExecutionListener extends AbstractTestExecutionListener {
|
||||||
|
|
||||||
@@ -75,13 +78,13 @@ public class MockitoResetTestExecutionListener extends AbstractTestExecutionList
|
|||||||
|
|
||||||
private void resetMocks(ConfigurableApplicationContext applicationContext, MockReset reset) {
|
private void resetMocks(ConfigurableApplicationContext applicationContext, MockReset reset) {
|
||||||
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
|
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
|
||||||
String[] names = beanFactory.getBeanDefinitionNames();
|
String[] beanNames = beanFactory.getBeanDefinitionNames();
|
||||||
Set<String> instantiatedSingletons = new HashSet<>(Arrays.asList(beanFactory.getSingletonNames()));
|
Set<String> instantiatedSingletons = new HashSet<>(Arrays.asList(beanFactory.getSingletonNames()));
|
||||||
for (String name : names) {
|
for (String beanName : beanNames) {
|
||||||
BeanDefinition definition = beanFactory.getBeanDefinition(name);
|
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
|
||||||
if (definition.isSingleton() && instantiatedSingletons.contains(name)) {
|
if (beanDefinition.isSingleton() && instantiatedSingletons.contains(beanName)) {
|
||||||
Object bean = getBean(beanFactory, name);
|
Object bean = getBean(beanFactory, beanName);
|
||||||
if (bean != null && reset.equals(MockReset.get(bean))) {
|
if (bean != null && reset == MockReset.get(bean)) {
|
||||||
Mockito.reset(bean);
|
Mockito.reset(bean);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,20 +101,20 @@ public class MockitoResetTestExecutionListener extends AbstractTestExecutionList
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Object getBean(ConfigurableListableBeanFactory beanFactory, String name) {
|
private Object getBean(ConfigurableListableBeanFactory beanFactory, String beanName) {
|
||||||
try {
|
try {
|
||||||
if (isStandardBeanOrSingletonFactoryBean(beanFactory, name)) {
|
if (isStandardBeanOrSingletonFactoryBean(beanFactory, beanName)) {
|
||||||
return beanFactory.getBean(name);
|
return beanFactory.getBean(beanName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
// Continue
|
// Continue
|
||||||
}
|
}
|
||||||
return beanFactory.getSingleton(name);
|
return beanFactory.getSingleton(beanName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isStandardBeanOrSingletonFactoryBean(ConfigurableListableBeanFactory beanFactory, String name) {
|
private boolean isStandardBeanOrSingletonFactoryBean(ConfigurableListableBeanFactory beanFactory, String beanName) {
|
||||||
String factoryBeanName = BeanFactory.FACTORY_BEAN_PREFIX + name;
|
String factoryBeanName = BeanFactory.FACTORY_BEAN_PREFIX + beanName;
|
||||||
if (beanFactory.containsBean(factoryBeanName)) {
|
if (beanFactory.containsBean(factoryBeanName)) {
|
||||||
FactoryBean<?> factoryBean = (FactoryBean<?>) beanFactory.getBean(factoryBeanName);
|
FactoryBean<?> factoryBean = (FactoryBean<?>) beanFactory.getBean(factoryBeanName);
|
||||||
return factoryBean.isSingleton();
|
return factoryBean.isSingleton();
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ import org.springframework.test.context.bean.override.BeanOverride;
|
|||||||
*
|
*
|
||||||
* @author Simon Baslé
|
* @author Simon Baslé
|
||||||
* @since 6.2
|
* @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)
|
@Target(ElementType.FIELD)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ import org.springframework.util.StringUtils;
|
|||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @since 6.2
|
* @since 6.2
|
||||||
*/
|
*/
|
||||||
class MockitoSpyBeanOverrideMetadata extends MockitoOverrideMetadata {
|
class MockitoSpyBeanOverrideMetadata extends AbstractMockitoOverrideMetadata {
|
||||||
|
|
||||||
MockitoSpyBeanOverrideMetadata(Field field, ResolvableType typeToSpy, MockitoSpyBean spyAnnotation) {
|
MockitoSpyBeanOverrideMetadata(Field field, ResolvableType typeToSpy, MockitoSpyBean spyAnnotation) {
|
||||||
this(field, typeToSpy, (StringUtils.hasText(spyAnnotation.name()) ? spyAnnotation.name() : null),
|
this(field, typeToSpy, (StringUtils.hasText(spyAnnotation.name()) ? spyAnnotation.name() : null),
|
||||||
@@ -62,17 +62,17 @@ class MockitoSpyBeanOverrideMetadata extends MockitoOverrideMetadata {
|
|||||||
@Nullable Object existingBeanInstance) {
|
@Nullable Object existingBeanInstance) {
|
||||||
|
|
||||||
Assert.notNull(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);
|
return createSpy(beanName, existingBeanInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <T> T createSpy(String name, Object instance) {
|
private Object createSpy(String name, Object instance) {
|
||||||
Class<?> resolvedTypeToOverride = getBeanType().resolve();
|
Class<?> resolvedTypeToOverride = getBeanType().resolve();
|
||||||
Assert.notNull(resolvedTypeToOverride, "Failed to resolve type to override");
|
Assert.notNull(resolvedTypeToOverride, "Failed to resolve type to override");
|
||||||
Assert.isInstanceOf(resolvedTypeToOverride, instance);
|
Assert.isInstanceOf(resolvedTypeToOverride, instance);
|
||||||
if (Mockito.mockingDetails(instance).isSpy()) {
|
if (Mockito.mockingDetails(instance).isSpy()) {
|
||||||
return (T) instance;
|
return instance;
|
||||||
}
|
}
|
||||||
MockSettings settings = MockReset.withSettings(getReset());
|
MockSettings settings = MockReset.withSettings(getReset());
|
||||||
if (StringUtils.hasLength(name)) {
|
if (StringUtils.hasLength(name)) {
|
||||||
@@ -91,7 +91,7 @@ class MockitoSpyBeanOverrideMetadata extends MockitoOverrideMetadata {
|
|||||||
settings.spiedInstance(instance);
|
settings.spiedInstance(instance);
|
||||||
toSpy = instance.getClass();
|
toSpy = instance.getClass();
|
||||||
}
|
}
|
||||||
return (T) Mockito.mock(toSpy, settings);
|
return Mockito.mock(toSpy, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,11 @@
|
|||||||
package org.springframework.test.context.bean.override.mockito;
|
package org.springframework.test.context.bean.override.mockito;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.AnnotatedElement;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.mockito.MockitoSession;
|
import org.mockito.MockitoSession;
|
||||||
@@ -31,7 +33,6 @@ import org.springframework.test.context.support.AbstractTestExecutionListener;
|
|||||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.util.ReflectionUtils.FieldCallback;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code TestExecutionListener} that enables {@link MockitoBean @MockitoBean}
|
* {@code TestExecutionListener} that enables {@link MockitoBean @MockitoBean}
|
||||||
@@ -41,7 +42,7 @@ import org.springframework.util.ReflectionUtils.FieldCallback;
|
|||||||
*
|
*
|
||||||
* <p>The {@link MockitoSession#setStrictness(Strictness) strictness} of the
|
* <p>The {@link MockitoSession#setStrictness(Strictness) strictness} of the
|
||||||
* session defaults to {@link Strictness#STRICT_STUBS}. Use
|
* session defaults to {@link Strictness#STRICT_STUBS}. Use
|
||||||
* {@link MockitoBeanSettings} to specify a different strictness.
|
* {@link MockitoBeanSettings @MockitoBeanSettings} to specify a different strictness.
|
||||||
*
|
*
|
||||||
* <p>The automatic reset support for {@code @MockBean} and {@code @SpyBean} is
|
* <p>The automatic reset support for {@code @MockBean} and {@code @SpyBean} is
|
||||||
* handled by the {@link MockitoResetTestExecutionListener}.
|
* handled by the {@link MockitoResetTestExecutionListener}.
|
||||||
@@ -50,8 +51,11 @@ import org.springframework.util.ReflectionUtils.FieldCallback;
|
|||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
* @author Moritz Halbritter
|
* @author Moritz Halbritter
|
||||||
|
* @author Sam Brannen
|
||||||
* @since 6.2
|
* @since 6.2
|
||||||
* @see MockitoResetTestExecutionListener
|
* @see MockitoResetTestExecutionListener
|
||||||
|
* @see MockitoBean @MockitoBean
|
||||||
|
* @see MockitoSpyBean @MockitoSpyBean
|
||||||
*/
|
*/
|
||||||
public class MockitoTestExecutionListener extends AbstractTestExecutionListener {
|
public class MockitoTestExecutionListener extends AbstractTestExecutionListener {
|
||||||
|
|
||||||
@@ -101,7 +105,7 @@ public class MockitoTestExecutionListener extends AbstractTestExecutionListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initMocks(TestContext testContext) {
|
private void initMocks(TestContext testContext) {
|
||||||
if (hasMockitoAnnotations(testContext)) {
|
if (MockitoAnnotationDetector.hasMockitoAnnotations(testContext.getTestClass())) {
|
||||||
Object testInstance = testContext.getTestInstance();
|
Object testInstance = testContext.getTestInstance();
|
||||||
MockitoBeanSettings annotation = AnnotationUtils.findAnnotation(testInstance.getClass(),
|
MockitoBeanSettings annotation = AnnotationUtils.findAnnotation(testInstance.getClass(),
|
||||||
MockitoBeanSettings.class);
|
MockitoBeanSettings.class);
|
||||||
@@ -124,51 +128,35 @@ public class MockitoTestExecutionListener extends AbstractTestExecutionListener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
* Utility class that detects {@code org.mockito} annotations as well as the
|
||||||
* annotations in this package (like {@link MockitoBeanSettings}).
|
* 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 static final String ORG_MOCKITO_PACKAGE = "org.mockito";
|
||||||
|
|
||||||
private final Set<Annotation> annotations = new LinkedHashSet<>();
|
private static final Predicate<Annotation> isMockitoAnnotation = annotation -> {
|
||||||
|
String packageName = annotation.annotationType().getPackageName();
|
||||||
|
return (packageName.startsWith(MOCKITO_BEAN_PACKAGE) ||
|
||||||
|
packageName.startsWith(ORG_MOCKITO_PACKAGE));
|
||||||
|
};
|
||||||
|
|
||||||
public void collect(Class<?> clazz) {
|
static boolean hasMockitoAnnotations(Class<?> testClass) {
|
||||||
ReflectionUtils.doWithFields(clazz, this);
|
Set<Annotation> annotations = new HashSet<>();
|
||||||
for (Annotation annotation : clazz.getAnnotations()) {
|
collect(testClass, annotations);
|
||||||
collect(annotation);
|
ReflectionUtils.doWithFields(testClass, field -> collect(field, annotations));
|
||||||
}
|
return !annotations.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
static void collect(AnnotatedElement annotatedElement, Set<Annotation> annotations) {
|
||||||
public void doWith(Field field) throws IllegalArgumentException {
|
Arrays.stream(annotatedElement.getAnnotations())
|
||||||
for (Annotation annotation : field.getAnnotations()) {
|
.filter(isMockitoAnnotation)
|
||||||
collect(annotation);
|
.forEach(annotations::add);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasAnnotations() {
|
|
||||||
return !this.annotations.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import java.lang.reflect.Field;
|
|||||||
import org.easymock.EasyMock;
|
import org.easymock.EasyMock;
|
||||||
import org.easymock.MockType;
|
import org.easymock.MockType;
|
||||||
|
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.config.SingletonBeanRegistry;
|
import org.springframework.beans.factory.config.SingletonBeanRegistry;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
@@ -62,17 +61,15 @@ class EasyMockBeanOverrideMetadata extends OverrideMetadata {
|
|||||||
getEasyMockBeans(singletonBeanRegistry).add(mock);
|
getEasyMockBeans(singletonBeanRegistry).add(mock);
|
||||||
}
|
}
|
||||||
|
|
||||||
private EasyMockBeans getEasyMockBeans(SingletonBeanRegistry singletonBeanRegistry) {
|
private static EasyMockBeans getEasyMockBeans(SingletonBeanRegistry singletonBeanRegistry) {
|
||||||
String className = EasyMockBeans.class.getName();
|
String beanName = EasyMockBeans.class.getName();
|
||||||
EasyMockBeans easyMockBeans = null;
|
EasyMockBeans easyMockBeans = null;
|
||||||
try {
|
if (singletonBeanRegistry.containsSingleton(beanName)) {
|
||||||
easyMockBeans = (EasyMockBeans) singletonBeanRegistry.getSingleton(className);
|
easyMockBeans = (EasyMockBeans) singletonBeanRegistry.getSingleton(beanName);
|
||||||
}
|
|
||||||
catch (NoSuchBeanDefinitionException ignored) {
|
|
||||||
}
|
}
|
||||||
if (easyMockBeans == null) {
|
if (easyMockBeans == null) {
|
||||||
easyMockBeans = new EasyMockBeans();
|
easyMockBeans = new EasyMockBeans();
|
||||||
singletonBeanRegistry.registerSingleton(className, easyMockBeans);
|
singletonBeanRegistry.registerSingleton(beanName, easyMockBeans);
|
||||||
}
|
}
|
||||||
return easyMockBeans;
|
return easyMockBeans;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class EasyMockBeanOverrideProcessor implements BeanOverrideProcessor {
|
|||||||
@Override
|
@Override
|
||||||
public OverrideMetadata createMetadata(Annotation annotation, Class<?> testClass, Field field) {
|
public OverrideMetadata createMetadata(Annotation annotation, Class<?> testClass, Field field) {
|
||||||
EasyMockBean easyMockBean = (EasyMockBean) annotation;
|
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());
|
return new EasyMockBeanOverrideMetadata(field, field.getType(), beanName, easyMockBean.mockType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.test.context.bean.override.easymock;
|
package org.springframework.test.context.bean.override.easymock;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
@@ -55,7 +56,15 @@ class EasyMockResetTestExecutionListener extends AbstractTestExecutionListener {
|
|||||||
|
|
||||||
private void resetMocks(ConfigurableApplicationContext applicationContext) {
|
private void resetMocks(ConfigurableApplicationContext applicationContext) {
|
||||||
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,8 +64,8 @@ public class MockitoBeanOverrideProcessorTests {
|
|||||||
|
|
||||||
assertThatIllegalStateException()
|
assertThatIllegalStateException()
|
||||||
.isThrownBy(() -> this.processor.createMetadata(annotation, clazz, field))
|
.isThrownBy(() -> this.processor.createMetadata(annotation, clazz, field))
|
||||||
.withMessage("Invalid annotation passed to MockitoBeanOverrideProcessor: expected " +
|
.withMessage("Invalid annotation passed to MockitoBeanOverrideProcessor: expected either " +
|
||||||
"@MockitoBean/@MockitoSpyBean on field %s.%s", field.getDeclaringClass().getName(),
|
"@MockitoBean or @MockitoSpyBean on field %s.%s", field.getDeclaringClass().getName(),
|
||||||
field.getName());
|
field.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user