Reject static Bean Override fields for @MockitoBean, @TestBean, etc.
Closes gh-33922
This commit is contained in:
parent
8b66d3c79a
commit
3569cfe990
|
@ -1,9 +1,10 @@
|
|||
[[spring-testing-annotation-beanoverriding-mockitobean]]
|
||||
= `@MockitoBean` and `@MockitoSpyBean`
|
||||
|
||||
`@MockitoBean` and `@MockitoSpyBean` are used on fields in test classes to override beans
|
||||
in the test's `ApplicationContext` with a Mockito _mock_ or _spy_, respectively. In the
|
||||
latter case, an early instance of the original bean is captured and wrapped by the spy.
|
||||
`@MockitoBean` and `@MockitoSpyBean` are used on non-static fields in test classes to
|
||||
override beans in the test's `ApplicationContext` with a Mockito _mock_ or _spy_,
|
||||
respectively. In the latter case, an early instance of the original bean is captured and
|
||||
wrapped by the spy.
|
||||
|
||||
By default, the annotated field's type is used to search for candidate beans to override.
|
||||
If multiple candidates match, `@Qualifier` can be provided to narrow the candidate to
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
[[spring-testing-annotation-beanoverriding-testbean]]
|
||||
= `@TestBean`
|
||||
|
||||
`@TestBean` is used on a field in a test class to override a specific bean in the test's
|
||||
`ApplicationContext` with an instance provided by a factory method.
|
||||
`@TestBean` is used on a non-static field in a test class to override a specific bean in
|
||||
the test's `ApplicationContext` with an instance provided by a factory method.
|
||||
|
||||
The associated factory method name is derived from the annotated field's name, or the
|
||||
bean name if specified. The factory method must be `static`, accept no arguments, and
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
= Bean Overriding in Tests
|
||||
|
||||
Bean overriding in tests refers to the ability to override specific beans in the
|
||||
`ApplicationContext` for a test class, by annotating one or more fields in the test class.
|
||||
`ApplicationContext` for a test class, by annotating one or more non-static fields in the
|
||||
test class.
|
||||
|
||||
NOTE: This feature is intended as a less risky alternative to the practice of registering
|
||||
a bean via `@Bean` with the `DefaultListableBeanFactory`
|
||||
|
@ -41,9 +42,10 @@ The `spring-test` module registers implementations of the latter two
|
|||
{spring-framework-code}/spring-test/src/main/resources/META-INF/spring.factories[`META-INF/spring.factories`
|
||||
properties file].
|
||||
|
||||
The bean overriding infrastructure searches in test classes for any field meta-annotated
|
||||
with `@BeanOverride` and instantiates the corresponding `BeanOverrideProcessor` which is
|
||||
responsible for creating an appropriate `BeanOverrideHandler`.
|
||||
The bean overriding infrastructure searches in test classes for any non-static field that
|
||||
is meta-annotated with `@BeanOverride` and instantiates the corresponding
|
||||
`BeanOverrideProcessor` which is responsible for creating an appropriate
|
||||
`BeanOverrideHandler`.
|
||||
|
||||
The internal `BeanOverrideBeanFactoryPostProcessor` then uses bean override handlers to
|
||||
alter the test's `ApplicationContext` by creating, replacing, or wrapping beans as
|
||||
|
|
|
@ -30,7 +30,7 @@ import org.springframework.aot.hint.annotation.Reflective;
|
|||
* <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
|
||||
* <p>Since the composed annotation should only be applied to non-static fields, it is
|
||||
* expected that it is meta-annotated with {@link Target @Target(ElementType.FIELD)}.
|
||||
*
|
||||
* <p>For concrete examples, see
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.springframework.test.context.bean.override;
|
|||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
@ -105,6 +106,8 @@ public abstract class BeanOverrideHandler {
|
|||
private static void processField(Field field, Class<?> testClass, List<BeanOverrideHandler> handlers) {
|
||||
AtomicBoolean overrideAnnotationFound = new AtomicBoolean();
|
||||
MergedAnnotations.from(field, DIRECT).stream(BeanOverride.class).forEach(mergedAnnotation -> {
|
||||
Assert.state(!Modifier.isStatic(field.getModifiers()),
|
||||
() -> "@BeanOverride field must not be static: " + field);
|
||||
MergedAnnotation<?> metaSource = mergedAnnotation.getMetaSource();
|
||||
Assert.state(metaSource != null, "@BeanOverride annotation must be meta-present");
|
||||
|
||||
|
|
|
@ -27,8 +27,8 @@ import org.springframework.core.annotation.AliasFor;
|
|||
import org.springframework.test.context.bean.override.BeanOverride;
|
||||
|
||||
/**
|
||||
* {@code @TestBean} is an annotation that can be applied to a field in a test
|
||||
* class to override a bean in the test's
|
||||
* {@code @TestBean} is an annotation that can be applied to a non-static field
|
||||
* in a test class to override a bean in the test's
|
||||
* {@link org.springframework.context.ApplicationContext ApplicationContext}
|
||||
* using a static factory method.
|
||||
*
|
||||
|
|
|
@ -29,8 +29,8 @@ import org.springframework.core.annotation.AliasFor;
|
|||
import org.springframework.test.context.bean.override.BeanOverride;
|
||||
|
||||
/**
|
||||
* {@code @MockitoBean} is an annotation that can be applied to a field in a test
|
||||
* class to override a bean in the test's
|
||||
* {@code @MockitoBean} is an annotation that can be applied to a non-static field
|
||||
* in a test class to override a bean in the test's
|
||||
* {@link org.springframework.context.ApplicationContext ApplicationContext}
|
||||
* using a Mockito mock.
|
||||
*
|
||||
|
|
|
@ -26,8 +26,10 @@ import org.springframework.core.annotation.AliasFor;
|
|||
import org.springframework.test.context.bean.override.BeanOverride;
|
||||
|
||||
/**
|
||||
* Mark a field to trigger a bean override using a Mockito spy, which will wrap
|
||||
* the original bean instance.
|
||||
* {@code @MockitoSpyBean} is an annotation that can be applied to a non-static
|
||||
* field in a test class to override a bean in the test's
|
||||
* {@link org.springframework.context.ApplicationContext ApplicationContext}
|
||||
* with a Mockito spy that wraps the original bean instance.
|
||||
*
|
||||
* <p>By default, the bean to spy is inferred from the type of the annotated
|
||||
* field. If multiple candidates exist, a {@code @Qualifier} annotation can be
|
||||
|
|
|
@ -86,6 +86,14 @@ class BeanOverrideHandlerTests {
|
|||
.withMessageContaining(faultyField.toString());
|
||||
}
|
||||
|
||||
@Test // gh-33922
|
||||
void forTestClassWithStaticBeanOverrideField() {
|
||||
Field staticField = field(StaticBeanOverrideField.class, "message");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> BeanOverrideHandler.forTestClass(StaticBeanOverrideField.class))
|
||||
.withMessage("@BeanOverride field must not be static: " + staticField);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanNameIsNullByDefault() {
|
||||
BeanOverrideHandler handler = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"));
|
||||
|
@ -246,6 +254,12 @@ class BeanOverrideHandlerTests {
|
|||
}
|
||||
}
|
||||
|
||||
static class StaticBeanOverrideField {
|
||||
|
||||
@DummyBean
|
||||
static String message;
|
||||
}
|
||||
|
||||
static class ConfigA {
|
||||
|
||||
ExampleService noQualifier;
|
||||
|
|
Loading…
Reference in New Issue