Skip to content

Commit 3569cfe

Browse files
committed
Reject static Bean Override fields for @⁠MockitoBean, @⁠TestBean, etc.
Closes gh-33922
1 parent 8b66d3c commit 3569cfe

File tree

9 files changed

+38
-16
lines changed

9 files changed

+38
-16
lines changed

framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-mockitobean.adoc

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
[[spring-testing-annotation-beanoverriding-mockitobean]]
22
= `@MockitoBean` and `@MockitoSpyBean`
33

4-
`@MockitoBean` and `@MockitoSpyBean` are used on fields in test classes to override beans
5-
in the test's `ApplicationContext` with a Mockito _mock_ or _spy_, respectively. In the
6-
latter case, an early instance of the original bean is captured and wrapped by the spy.
4+
`@MockitoBean` and `@MockitoSpyBean` are used on non-static fields in test classes to
5+
override beans in the test's `ApplicationContext` with a Mockito _mock_ or _spy_,
6+
respectively. In the latter case, an early instance of the original bean is captured and
7+
wrapped by the spy.
78

89
By default, the annotated field's type is used to search for candidate beans to override.
910
If multiple candidates match, `@Qualifier` can be provided to narrow the candidate to

framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-testbean.adoc

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[[spring-testing-annotation-beanoverriding-testbean]]
22
= `@TestBean`
33

4-
`@TestBean` is used on a field in a test class to override a specific bean in the test's
5-
`ApplicationContext` with an instance provided by a factory method.
4+
`@TestBean` is used on a non-static field in a test class to override a specific bean in
5+
the test's `ApplicationContext` with an instance provided by a factory method.
66

77
The associated factory method name is derived from the annotated field's name, or the
88
bean name if specified. The factory method must be `static`, accept no arguments, and

framework-docs/modules/ROOT/pages/testing/testcontext-framework/bean-overriding.adoc

+6-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
= Bean Overriding in Tests
33

44
Bean overriding in tests refers to the ability to override specific beans in the
5-
`ApplicationContext` for a test class, by annotating one or more fields in the test class.
5+
`ApplicationContext` for a test class, by annotating one or more non-static fields in the
6+
test class.
67

78
NOTE: This feature is intended as a less risky alternative to the practice of registering
89
a bean via `@Bean` with the `DefaultListableBeanFactory`
@@ -41,9 +42,10 @@ The `spring-test` module registers implementations of the latter two
4142
{spring-framework-code}/spring-test/src/main/resources/META-INF/spring.factories[`META-INF/spring.factories`
4243
properties file].
4344

44-
The bean overriding infrastructure searches in test classes for any field meta-annotated
45-
with `@BeanOverride` and instantiates the corresponding `BeanOverrideProcessor` which is
46-
responsible for creating an appropriate `BeanOverrideHandler`.
45+
The bean overriding infrastructure searches in test classes for any non-static field that
46+
is meta-annotated with `@BeanOverride` and instantiates the corresponding
47+
`BeanOverrideProcessor` which is responsible for creating an appropriate
48+
`BeanOverrideHandler`.
4749

4850
The internal `BeanOverrideBeanFactoryPostProcessor` then uses bean override handlers to
4951
alter the test's `ApplicationContext` by creating, replacing, or wrapping beans as

spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverride.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
* <p>Specifying this annotation registers the configured {@link BeanOverrideProcessor}
3131
* which must be capable of handling the composed annotation and its attributes.
3232
*
33-
* <p>Since the composed annotation should only be applied to fields, it is
33+
* <p>Since the composed annotation should only be applied to non-static fields, it is
3434
* expected that it is meta-annotated with {@link Target @Target(ElementType.FIELD)}.
3535
*
3636
* <p>For concrete examples, see

spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideHandler.java

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.lang.annotation.Annotation;
2020
import java.lang.reflect.Field;
21+
import java.lang.reflect.Modifier;
2122
import java.util.Arrays;
2223
import java.util.Collections;
2324
import java.util.HashSet;
@@ -105,6 +106,8 @@ public static List<BeanOverrideHandler> forTestClass(Class<?> testClass) {
105106
private static void processField(Field field, Class<?> testClass, List<BeanOverrideHandler> handlers) {
106107
AtomicBoolean overrideAnnotationFound = new AtomicBoolean();
107108
MergedAnnotations.from(field, DIRECT).stream(BeanOverride.class).forEach(mergedAnnotation -> {
109+
Assert.state(!Modifier.isStatic(field.getModifiers()),
110+
() -> "@BeanOverride field must not be static: " + field);
108111
MergedAnnotation<?> metaSource = mergedAnnotation.getMetaSource();
109112
Assert.state(metaSource != null, "@BeanOverride annotation must be meta-present");
110113

spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBean.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
import org.springframework.test.context.bean.override.BeanOverride;
2828

2929
/**
30-
* {@code @TestBean} is an annotation that can be applied to a field in a test
31-
* class to override a bean in the test's
30+
* {@code @TestBean} is an annotation that can be applied to a non-static field
31+
* in a test class to override a bean in the test's
3232
* {@link org.springframework.context.ApplicationContext ApplicationContext}
3333
* using a static factory method.
3434
*

spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBean.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
import org.springframework.test.context.bean.override.BeanOverride;
3030

3131
/**
32-
* {@code @MockitoBean} is an annotation that can be applied to a field in a test
33-
* class to override a bean in the test's
32+
* {@code @MockitoBean} is an annotation that can be applied to a non-static field
33+
* in a test class to override a bean in the test's
3434
* {@link org.springframework.context.ApplicationContext ApplicationContext}
3535
* using a Mockito mock.
3636
*

spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBean.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@
2626
import org.springframework.test.context.bean.override.BeanOverride;
2727

2828
/**
29-
* Mark a field to trigger a bean override using a Mockito spy, which will wrap
30-
* the original bean instance.
29+
* {@code @MockitoSpyBean} is an annotation that can be applied to a non-static
30+
* field in a test class to override a bean in the test's
31+
* {@link org.springframework.context.ApplicationContext ApplicationContext}
32+
* with a Mockito spy that wraps the original bean instance.
3133
*
3234
* <p>By default, the bean to spy is inferred from the type of the annotated
3335
* field. If multiple candidates exist, a {@code @Qualifier} annotation can be

spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideHandlerTests.java

+14
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ void forTestClassWithCompetingBeanOverrideAnnotationsOnSameField() {
8686
.withMessageContaining(faultyField.toString());
8787
}
8888

89+
@Test // gh-33922
90+
void forTestClassWithStaticBeanOverrideField() {
91+
Field staticField = field(StaticBeanOverrideField.class, "message");
92+
assertThatIllegalStateException()
93+
.isThrownBy(() -> BeanOverrideHandler.forTestClass(StaticBeanOverrideField.class))
94+
.withMessage("@BeanOverride field must not be static: " + staticField);
95+
}
96+
8997
@Test
9098
void getBeanNameIsNullByDefault() {
9199
BeanOverrideHandler handler = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"));
@@ -246,6 +254,12 @@ static String foo() {
246254
}
247255
}
248256

257+
static class StaticBeanOverrideField {
258+
259+
@DummyBean
260+
static String message;
261+
}
262+
249263
static class ConfigA {
250264

251265
ExampleService noQualifier;

0 commit comments

Comments
 (0)