Skip to content

Commit 0e8316e

Browse files
committed
Add value attribute alias to @⁠MockitoBean and @⁠MockitoSpyBean
This commit improves the user experience for common use cases by introducing new `value` attributes in @⁠MockitoBean and @⁠MockitoSpyBean that are aliases for the existing `name` attributes. For example, this allows developers to declare @⁠MockitoBean("userService") instead of @⁠MockitoBean(name = "userService"), analogous to the existing name/value alias support in @⁠TestBean. Closes gh-33680
1 parent 1b8f2c4 commit 0e8316e

File tree

12 files changed

+97
-68
lines changed

12 files changed

+97
-68
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ Java::
7777
[source,java,indent=0,subs="verbatim,quotes"]
7878
----
7979
class OverrideBeanTests {
80-
@MockitoBean(name = "service") // <1>
80+
@MockitoBean("service") // <1>
8181
private CustomService customService;
8282
8383
// test case body...
@@ -121,7 +121,7 @@ Java::
121121
[source,java,indent=0,subs="verbatim,quotes"]
122122
----
123123
class OverrideBeanTests {
124-
@MockitoSpyBean(name = "service") // <1>
124+
@MockitoSpyBean("service") // <1>
125125
private CustomService customService;
126126
127127
// test case body...

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

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.mockito.Answers;
2626
import org.mockito.MockSettings;
2727

28+
import org.springframework.core.annotation.AliasFor;
2829
import org.springframework.test.context.bean.override.BeanOverride;
2930

3031
/**
@@ -33,12 +34,12 @@
3334
* {@link org.springframework.context.ApplicationContext ApplicationContext}
3435
* using a Mockito mock.
3536
*
36-
* <p>By default, the bean to override is inferred from the type of the annotated
37+
* <p>By default, the bean to mock is inferred from the type of the annotated
3738
* field. If multiple candidates exist, a {@code @Qualifier} annotation can be
3839
* used to help disambiguate. In the absence of a {@code @Qualifier} annotation,
3940
* the name of the annotated field will be used as a fallback qualifier.
40-
* Alternatively, you can explicitly specify a bean name to replace by setting the
41-
* {@link #name() name} attribute.
41+
* Alternatively, you can explicitly specify a bean name to mock by setting the
42+
* {@link #value() value} or {@link #name() name} attribute.
4243
*
4344
* <p>A new bean definition will be created if a corresponding bean definition does
4445
* not exist. However, if you would like for the test to fail when a corresponding
@@ -52,7 +53,7 @@
5253
* the context alongside the existing dependency.
5354
*
5455
* <p><strong>NOTE</strong>: Only <em>singleton</em> beans can be overridden.
55-
* Any attempt to override a non-singleton bean will result in an exception.
56+
* Any attempt to mock a non-singleton bean will result in an exception.
5657
*
5758
* @author Simon Baslé
5859
* @author Sam Brannen
@@ -67,12 +68,22 @@
6768
public @interface MockitoBean {
6869

6970
/**
70-
* The name of the bean to register or replace.
71-
* <p>If left unspecified, the bean to override is selected according to
72-
* the annotated field's type, taking qualifiers into account if necessary.
73-
* See the {@linkplain MockitoBean class-level documentation} for details.
74-
* @return the name of the mocked bean
71+
* Alias for {@link #name()}.
72+
* <p>Intended to be used when no other attributes are needed &mdash; for
73+
* example, {@code @MockitoBean("customBeanName")}.
74+
* @see #name()
7575
*/
76+
@AliasFor("name")
77+
String value() default "";
78+
79+
/**
80+
* Name of the bean to mock.
81+
* <p>If left unspecified, the bean to mock is selected according to the
82+
* annotated field's type, taking qualifiers into account if necessary. See
83+
* the {@linkplain MockitoBean class-level documentation} for details.
84+
* @see #value()
85+
*/
86+
@AliasFor("value")
7687
String name() default "";
7788

7889
/**

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

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,32 @@
2424

2525
import org.mockito.Mockito;
2626

27+
import org.springframework.core.annotation.AliasFor;
2728
import org.springframework.test.context.bean.override.BeanOverride;
2829

2930
/**
3031
* Mark a field to trigger a bean override using a Mockito spy, which will wrap
31-
* the original instance.
32+
* the original bean instance.
3233
*
33-
* <p>If no explicit {@link #name()} is specified, a target bean is selected
34-
* according to the class of the annotated field, and there must be exactly one
35-
* such candidate bean. A {@code @Qualifier} annotation can be used to help
36-
* disambiguate.
37-
* If a {@link #name()} is specified, it is required that a target bean of that
38-
* name has been previously registered in the application context.
34+
* <p>By default, the bean to spy is inferred from the type of the annotated
35+
* field. If multiple candidates exist, a {@code @Qualifier} annotation can be
36+
* used to help disambiguate. In the absence of a {@code @Qualifier} annotation,
37+
* the name of the annotated field will be used as a fallback qualifier.
38+
* Alternatively, you can explicitly specify a bean name to spy by setting the
39+
* {@link #value() value} or {@link #name() name} attribute. If a bean name is
40+
* specified, it is required that a target bean with that name has been previously
41+
* registered in the application context.
3942
*
40-
* <p>Dependencies that are known to the application context but are not beans
41-
* (such as those
43+
* <p>A spy cannot be created for components which are known to the application
44+
* context but are not beans &mdash; for example, components
4245
* {@link org.springframework.beans.factory.config.ConfigurableListableBeanFactory#registerResolvableDependency(Class, Object)
43-
* registered directly}) will not be found.
46+
* registered directly} as resolvable dependencies.
4447
*
45-
* <p><strong>NOTE</strong>: Only <em>singleton</em> beans can be overridden.
46-
* Any attempt to override a non-singleton bean will result in an exception.
48+
* <p><strong>NOTE</strong>: Only <em>singleton</em> beans can be spied.
49+
* Any attempt to create a spy for a non-singleton bean will result in an exception.
4750
*
4851
* @author Simon Baslé
52+
* @author Sam Brannen
4953
* @since 6.2
5054
* @see org.springframework.test.context.bean.override.mockito.MockitoBean @MockitoBean
5155
* @see org.springframework.test.context.bean.override.convention.TestBean @TestBean
@@ -57,12 +61,22 @@
5761
public @interface MockitoSpyBean {
5862

5963
/**
60-
* The name of the bean to spy.
61-
* <p>If left unspecified, the bean to spy is selected according to
62-
* the annotated field's type, taking qualifiers into account if necessary.
63-
* See the {@linkplain MockitoSpyBean class-level documentation} for details.
64-
* @return the name of the spied bean
64+
* Alias for {@link #name()}.
65+
* <p>Intended to be used when no other attributes are needed &mdash; for
66+
* example, {@code @MockitoSpyBean("customBeanName")}.
67+
* @see #name()
6568
*/
69+
@AliasFor("name")
70+
String value() default "";
71+
72+
/**
73+
* Name of the bean to spy.
74+
* <p>If left unspecified, the bean to spy is selected according to the
75+
* annotated field's type, taking qualifiers into account if necessary. See
76+
* the {@linkplain MockitoSpyBean class-level documentation} for details.
77+
* @see #value()
78+
*/
79+
@AliasFor("value")
6680
String name() default "";
6781

6882
/**

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@
1717
package org.springframework.test.context.bean.override.mockito;
1818

1919
import org.junit.jupiter.api.Test;
20-
import org.mockito.Answers;
2120

2221
import org.springframework.test.context.ContextCustomizer;
2322
import org.springframework.test.context.bean.override.BeanOverrideContextCustomizerTestUtils;
2423

2524
import static org.assertj.core.api.Assertions.assertThat;
25+
import static org.mockito.Answers.RETURNS_MOCKS;
2626

2727
/**
2828
* Tests that validate the behavior of {@link MockitoBean} and
@@ -85,7 +85,7 @@ private ContextCustomizer createContextCustomizer(Class<?> testClass) {
8585

8686
static class Case1ByName {
8787

88-
@MockitoBean(name = "serviceBean")
88+
@MockitoBean("serviceBean")
8989
private String exampleService;
9090

9191
}
@@ -99,7 +99,7 @@ static class Case1ByType {
9999

100100
static class Case2ByName {
101101

102-
@MockitoBean(name = "serviceBean")
102+
@MockitoBean("serviceBean")
103103
private String serviceToMock;
104104

105105
}
@@ -120,14 +120,14 @@ static class Case2ByTypeSameFieldName {
120120

121121
static class Case3 {
122122

123-
@MockitoBean(answers = Answers.RETURNS_MOCKS)
123+
@MockitoBean(answers = RETURNS_MOCKS)
124124
private String exampleService;
125125

126126
}
127127

128128
static class Case4ByName {
129129

130-
@MockitoSpyBean(name = "serviceBean")
130+
@MockitoSpyBean("serviceBean")
131131
private String exampleService;
132132

133133
}
@@ -141,7 +141,7 @@ static class Case4ByType {
141141

142142
static class Case5ByName {
143143

144-
@MockitoSpyBean(name = "serviceBean")
144+
@MockitoSpyBean("serviceBean")
145145
private String serviceToMock;
146146

147147
}

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,22 @@
3636
@SpringJUnitConfig
3737
public class MockitoBeanForByNameLookupIntegrationTests {
3838

39-
@MockitoBean(name = "field")
39+
@MockitoBean("field")
4040
ExampleService field;
4141

42-
@MockitoBean(name = "nestedField")
42+
@MockitoBean("nestedField")
4343
ExampleService nestedField;
4444

45-
@MockitoBean(name = "field")
45+
@MockitoBean("field")
4646
ExampleService renamed1;
4747

48-
@MockitoBean(name = "nestedField")
48+
@MockitoBean("nestedField")
4949
ExampleService renamed2;
5050

51-
@MockitoBean(name = "nonExistingBean")
51+
@MockitoBean("nonExistingBean")
5252
ExampleService nonExisting1;
5353

54-
@MockitoBean(name = "nestedNonExistingBean")
54+
@MockitoBean("nestedNonExistingBean")
5555
ExampleService nonExisting2;
5656

5757

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.mockito.Answers;
2525

2626
import org.springframework.core.ResolvableType;
27+
import org.springframework.core.annotation.AnnotatedElementUtils;
2728
import org.springframework.test.context.bean.override.OverrideMetadata;
2829
import org.springframework.util.ReflectionUtils;
2930

@@ -114,7 +115,7 @@ private Field sampleField(String fieldName) {
114115
}
115116

116117
private MockitoBeanOverrideMetadata createMetadata(Field field) {
117-
MockitoBean annotation = field.getAnnotation(MockitoBean.class);
118+
MockitoBean annotation = AnnotatedElementUtils.getMergedAnnotation(field, MockitoBean.class);
118119
return new MockitoBeanOverrideMetadata(field, ResolvableType.forClass(field.getType()), annotation);
119120
}
120121

@@ -128,7 +129,7 @@ static class SampleOneMock {
128129

129130
static class SampleOneMockWithName {
130131

131-
@MockitoBean(name = "anotherService")
132+
@MockitoBean("anotherService")
132133
String service;
133134

134135
}
@@ -144,7 +145,7 @@ static class Sample {
144145
@MockitoBean(name = "beanToMock")
145146
private String service3;
146147

147-
@MockitoBean(name = "beanToMock")
148+
@MockitoBean(value = "beanToMock")
148149
private String service4;
149150

150151
@MockitoBean(extraInterfaces = Externalizable.class)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import org.junit.platform.testkit.engine.EngineTestKit;
2727
import org.junit.platform.testkit.engine.Events;
2828
import org.mockito.exceptions.misusing.UnnecessaryStubbingException;
29-
import org.mockito.quality.Strictness;
3029

3130
import org.springframework.context.annotation.Configuration;
3231
import org.springframework.test.annotation.DirtiesContext;
@@ -42,6 +41,7 @@
4241
import static org.mockito.ArgumentMatchers.anyInt;
4342
import static org.mockito.BDDMockito.when;
4443
import static org.mockito.Mockito.mock;
44+
import static org.mockito.quality.Strictness.STRICT_STUBS;
4545

4646
/**
4747
* Integration tests ensuring unnecessary stubbing is reported in various
@@ -85,7 +85,7 @@ void unnecessaryStub() {
8585

8686
@SpringJUnitConfig(Config.class)
8787
@DirtiesContext
88-
@MockitoBeanSettings(Strictness.STRICT_STUBS)
88+
@MockitoBeanSettings(STRICT_STUBS)
8989
static class ExplicitStrictness extends BaseCase {
9090
}
9191

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
import static org.assertj.core.api.Assertions.assertThat;
3535
import static org.mockito.Mockito.doReturn;
36+
import static org.springframework.test.context.bean.override.mockito.MockReset.BEFORE;
3637

3738
/**
3839
* Integration tests for {@link MockitoBean} that validate automatic reset
@@ -45,10 +46,10 @@
4546
@TestMethodOrder(OrderAnnotation.class)
4647
public class MockitoBeanWithResetIntegrationTests {
4748

48-
@MockitoBean(reset = MockReset.BEFORE)
49+
@MockitoBean(reset = BEFORE)
4950
ExampleService service;
5051

51-
@MockitoBean(reset = MockReset.BEFORE)
52+
@MockitoBean(reset = BEFORE)
5253
FailingExampleService failingService;
5354

5455
@Order(1)

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,16 @@
4040
@SpringJUnitConfig(Config.class)
4141
public class MockitoSpyBeanForByNameLookupIntegrationTests {
4242

43-
@MockitoSpyBean(name = "field")
43+
@MockitoSpyBean("field")
4444
ExampleService field;
4545

46-
@MockitoSpyBean(name = "nestedField")
46+
@MockitoSpyBean("nestedField")
4747
ExampleService nestedField;
4848

49-
@MockitoSpyBean(name = "field")
49+
@MockitoSpyBean("field")
5050
ExampleService renamed1;
5151

52-
@MockitoSpyBean(name = "nestedField")
52+
@MockitoSpyBean("nestedField")
5353
ExampleService renamed2;
5454

5555

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.junit.jupiter.api.Test;
2323

2424
import org.springframework.core.ResolvableType;
25+
import org.springframework.core.annotation.AnnotatedElementUtils;
2526
import org.springframework.test.context.bean.override.OverrideMetadata;
2627
import org.springframework.util.ReflectionUtils;
2728

@@ -105,7 +106,7 @@ private Field sampleField(String fieldName) {
105106
}
106107

107108
private MockitoSpyBeanOverrideMetadata createMetadata(Field field) {
108-
MockitoSpyBean annotation = field.getAnnotation(MockitoSpyBean.class);
109+
MockitoSpyBean annotation = AnnotatedElementUtils.getMergedAnnotation(field, MockitoSpyBean.class);
109110
return new MockitoSpyBeanOverrideMetadata(field, ResolvableType.forClass(field.getType()), annotation);
110111
}
111112

@@ -119,7 +120,7 @@ static class SampleOneSpy {
119120

120121
static class SampleOneSpyWithName {
121122

122-
@MockitoSpyBean(name = "anotherService")
123+
@MockitoSpyBean("anotherService")
123124
String service;
124125

125126
}
@@ -132,10 +133,10 @@ static class Sample {
132133
@MockitoSpyBean
133134
private String service2;
134135

135-
@MockitoSpyBean(name = "beanToMock")
136+
@MockitoSpyBean(name = "beanToSpy")
136137
private String service3;
137138

138-
@MockitoSpyBean(name = "beanToMock")
139+
@MockitoSpyBean(value = "beanToSpy")
139140
private String service4;
140141

141142
@MockitoSpyBean(reset = MockReset.BEFORE)

0 commit comments

Comments
 (0)