Skip to content

Commit 45e9f04

Browse files
committed
Remove TestOverrideMetadata and use dummy implementation
TestOverrideMetadata was doing byName lookup by default, even without a bean name. This makes writing tests that need to do by type lookup way more complicated that it should. This commit introduces DummyBean, that merely replaces two types with hardcoded values. It also exposes the raw attribute of OverrideMetadata and doesn't override anything from it to make sure the behavior is not altered. Closes gh-32982
1 parent 457bf94 commit 45e9f04

8 files changed

+185
-466
lines changed

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

+85-131
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,10 @@
3333
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
3434
import org.springframework.beans.factory.support.RootBeanDefinition;
3535
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
36-
import org.springframework.context.annotation.Bean;
37-
import org.springframework.context.annotation.Configuration;
3836
import org.springframework.context.support.SimpleThreadScope;
3937
import org.springframework.core.Ordered;
4038
import org.springframework.core.ResolvableType;
4139
import org.springframework.test.context.MergedContextConfiguration;
42-
import org.springframework.test.context.bean.override.example.ExampleBeanOverrideAnnotation;
43-
import org.springframework.test.context.bean.override.example.ExampleService;
44-
import org.springframework.test.context.bean.override.example.FailingExampleService;
45-
import org.springframework.test.context.bean.override.example.RealExampleService;
4640
import org.springframework.test.util.ReflectionTestUtils;
4741
import org.springframework.util.Assert;
4842

@@ -56,123 +50,123 @@
5650
* {@link BeanOverrideRegistrar}.
5751
*
5852
* @author Simon Baslé
53+
* @author Stephane Nicoll
5954
*/
6055
class BeanOverrideBeanFactoryPostProcessorTests {
6156

6257
@Test
6358
void canReplaceExistingBeanDefinitions() {
64-
AnnotationConfigApplicationContext context = createContext(ReplaceBeans.class);
65-
context.register(ReplaceBeans.class);
66-
context.registerBean("explicit", ExampleService.class, () -> new RealExampleService("unexpected"));
67-
context.registerBean("implicitName", ExampleService.class, () -> new RealExampleService("unexpected"));
68-
59+
AnnotationConfigApplicationContext context = createContext(CaseByNameAndByType.class);
60+
context.registerBean("descriptionBean", String.class, () -> "Original");
61+
context.registerBean("someInteger", Integer.class, () -> 1);
6962
context.refresh();
7063

71-
assertThat(context.getBean("explicit")).isSameAs(OVERRIDE_SERVICE);
72-
assertThat(context.getBean("implicitName")).isSameAs(OVERRIDE_SERVICE);
64+
assertThat(context.getBean("descriptionBean")).isEqualTo("overridden");
65+
assertThat(context.getBean("someInteger")).isSameAs(42);
7366
}
7467

7568
@Test
76-
void cannotReplaceIfNoBeanMatching() {
77-
AnnotationConfigApplicationContext context = createContext(ReplaceBeans.class);
78-
context.register(ReplaceBeans.class);
79-
//note we don't register any original bean here
69+
void cannotReplaceIfNoBeanNameMatching() {
70+
AnnotationConfigApplicationContext context = createContext(CaseByName.class);
8071

8172
assertThatIllegalStateException()
8273
.isThrownBy(context::refresh)
83-
.withMessage("Unable to override bean 'explicit': there is no bean definition " +
84-
"to replace with that name of type org.springframework.test.context.bean.override.example.ExampleService");
74+
.withMessage("Unable to override bean 'descriptionBean': there is no bean definition " +
75+
"to replace with that name of type java.lang.String");
8576
}
8677

8778
@Test
88-
void canReplaceExistingBeanDefinitionsWithCreateReplaceStrategy() {
89-
AnnotationConfigApplicationContext context = createContext(CreateIfOriginalIsMissingBean.class);
90-
context.register(CreateIfOriginalIsMissingBean.class);
91-
context.registerBean("explicit", ExampleService.class, () -> new RealExampleService("unexpected"));
92-
context.registerBean("implicitName", ExampleService.class, () -> new RealExampleService("unexpected"));
79+
void cannotReplaceIfNoBeanTypeMatching() {
80+
AnnotationConfigApplicationContext context = createContext(CaseByType.class);
9381

82+
assertThatIllegalStateException()
83+
.isThrownBy(context::refresh)
84+
.withMessage("Unable to override bean: no bean definitions of type java.lang.Integer " +
85+
"(as required by annotated field 'CaseByType.counter')");
86+
}
87+
88+
@Test
89+
void canReplaceExistingBeanDefinitionsWithCreateReplaceStrategy() {
90+
AnnotationConfigApplicationContext context = createContext(CaseByNameAndByTypeWithReplaceOrCreateStrategy.class);
91+
context.register(CaseByNameAndByTypeWithReplaceOrCreateStrategy.class);
92+
context.registerBean("descriptionBean", String.class, () -> "Original");
93+
context.registerBean("someInteger", Integer.class, () -> 1);
9494
context.refresh();
9595

96-
assertThat(context.getBean("explicit")).isSameAs(OVERRIDE_SERVICE);
97-
assertThat(context.getBean("implicitName")).isSameAs(OVERRIDE_SERVICE);
96+
assertThat(context.getBean("descriptionBean")).isEqualTo("overridden");
97+
assertThat(context.getBean("someInteger")).isEqualTo(42);
9898
}
9999

100100
@Test
101101
void canCreateIfOriginalMissingWithCreateReplaceStrategy() {
102-
AnnotationConfigApplicationContext context = createContext(CreateIfOriginalIsMissingBean.class);
103-
context.register(CreateIfOriginalIsMissingBean.class);
104-
//note we don't register original beans here
105-
102+
AnnotationConfigApplicationContext context = createContext(CaseByNameAndByTypeWithReplaceOrCreateStrategy.class);
106103
context.refresh();
107104

108-
assertThat(context.getBean("explicit")).isSameAs(OVERRIDE_SERVICE);
109-
assertThat(context.getBean("implicitName")).isSameAs(OVERRIDE_SERVICE);
105+
String byTypeGeneratedBeanName = "java.lang.Integer#0";
106+
assertThat(context.getBeanDefinitionNames()).contains("descriptionBean", byTypeGeneratedBeanName);
107+
assertThat(context.getBean("descriptionBean")).isEqualTo("overridden");
108+
assertThat(context.getBean(byTypeGeneratedBeanName)).isEqualTo(42);
110109
}
111110

112111
@Test
113112
void canOverrideBeanProducedByFactoryBeanWithClassObjectTypeAttribute() {
114-
AnnotationConfigApplicationContext context = createContext(OverriddenFactoryBean.class);
115-
RootBeanDefinition factoryBeanDefinition = new RootBeanDefinition(TestFactoryBean.class);
116-
factoryBeanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, SomeInterface.class);
117-
context.registerBeanDefinition("beanToBeOverridden", factoryBeanDefinition);
118-
context.register(OverriddenFactoryBean.class);
119-
113+
AnnotationConfigApplicationContext context = prepareContextWithFactoryBean(CharSequence.class);
120114
context.refresh();
121115

122-
assertThat(context.getBean("beanToBeOverridden")).isSameAs(OVERRIDE);
116+
assertThat(context.getBean("beanToBeOverridden")).isEqualTo("overridden");
123117
}
124118

125119
@Test
126120
void canOverrideBeanProducedByFactoryBeanWithResolvableTypeObjectTypeAttribute() {
127-
AnnotationConfigApplicationContext context = createContext(OverriddenFactoryBean.class);
128-
RootBeanDefinition factoryBeanDefinition = new RootBeanDefinition(TestFactoryBean.class);
129-
ResolvableType objectType = ResolvableType.forClass(SomeInterface.class);
130-
factoryBeanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, objectType);
131-
context.registerBeanDefinition("beanToBeOverridden", factoryBeanDefinition);
132-
context.register(OverriddenFactoryBean.class);
133-
121+
AnnotationConfigApplicationContext context = prepareContextWithFactoryBean(ResolvableType.forClass(CharSequence.class));
134122
context.refresh();
135123

136-
assertThat(context.getBean("beanToBeOverridden")).isSameAs(OVERRIDE);
124+
assertThat(context.getBean("beanToBeOverridden")).isEqualTo("overridden");
125+
}
126+
127+
private AnnotationConfigApplicationContext prepareContextWithFactoryBean(Object objectTypeAttribute) {
128+
AnnotationConfigApplicationContext context = createContext(CaseOverrideBeanProducedByFactoryBean.class);
129+
context.registerBean("testFactoryBean", TestFactoryBean.class, TestFactoryBean::new);
130+
RootBeanDefinition factoryBeanDefinition = new RootBeanDefinition(TestFactoryBean.class);
131+
factoryBeanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, objectTypeAttribute);
132+
context.registerBeanDefinition("beanToBeOverridden", factoryBeanDefinition);
133+
return context;
137134
}
138135

139136
@Test
140137
void postProcessorShouldNotTriggerEarlyInitialization() {
141-
AnnotationConfigApplicationContext context = createContext(EagerInitBean.class);
138+
AnnotationConfigApplicationContext context = createContext(CaseByTypeWithReplaceOrCreateStrategy.class);
142139

143140
context.register(FactoryBeanRegisteringPostProcessor.class);
144141
context.register(EarlyBeanInitializationDetector.class);
145-
context.register(EagerInitBean.class);
146142

147143
assertThatNoException().isThrownBy(context::refresh);
148144
}
149145

150146
@Test
151147
void allowReplaceDefinitionWhenSingletonDefinitionPresent() {
152-
AnnotationConfigApplicationContext context = createContext(SingletonBean.class);
148+
AnnotationConfigApplicationContext context = createContext(CaseByName.class);
153149
RootBeanDefinition definition = new RootBeanDefinition(String.class, () -> "ORIGINAL");
154150
definition.setScope(BeanDefinition.SCOPE_SINGLETON);
155-
context.registerBeanDefinition("singleton", definition);
156-
context.register(SingletonBean.class);
151+
context.registerBeanDefinition("descriptionBean", definition);
157152

158153
assertThatNoException().isThrownBy(context::refresh);
159-
assertThat(context.isSingleton("singleton")).as("isSingleton").isTrue();
160-
assertThat(context.getBean("singleton")).as("overridden").isEqualTo("USED THIS");
154+
assertThat(context.isSingleton("descriptionBean")).as("isSingleton").isTrue();
155+
assertThat(context.getBean("descriptionBean")).isEqualTo("overridden");
161156
}
162157

163158
@Test
164159
void copyDefinitionPrimaryFallbackAndScope() {
165-
AnnotationConfigApplicationContext context = createContext(SingletonBean.class);
160+
AnnotationConfigApplicationContext context = createContext(CaseByName.class);
166161
context.getBeanFactory().registerScope("customScope", new SimpleThreadScope());
167162
RootBeanDefinition definition = new RootBeanDefinition(String.class, () -> "ORIGINAL");
168163
definition.setScope("customScope");
169164
definition.setPrimary(true);
170165
definition.setFallback(true);
171-
context.registerBeanDefinition("singleton", definition);
172-
context.register(SingletonBean.class);
166+
context.registerBeanDefinition("descriptionBean", definition);
173167

174168
assertThatNoException().isThrownBy(context::refresh);
175-
assertThat(context.getBeanDefinition("singleton"))
169+
assertThat(context.getBeanDefinition("descriptionBean"))
176170
.isNotSameAs(definition)
177171
.matches(BeanDefinition::isPrimary, "isPrimary")
178172
.matches(BeanDefinition::isFallback, "isFallback")
@@ -183,22 +177,20 @@ void copyDefinitionPrimaryFallbackAndScope() {
183177

184178
@Test
185179
void createDefinitionShouldSetQualifierElement() {
186-
AnnotationConfigApplicationContext context = createContext(QualifiedBean.class);
187-
context.registerBeanDefinition("singleton", new RootBeanDefinition(String.class, () -> "ORIGINAL"));
188-
context.register(QualifiedBean.class);
180+
AnnotationConfigApplicationContext context = createContext(CaseByNameWithQualifier.class);
181+
context.registerBeanDefinition("descriptionBean", new RootBeanDefinition(String.class, () -> "ORIGINAL"));
189182

190183
assertThatNoException().isThrownBy(context::refresh);
191-
192-
assertThat(context.getBeanDefinition("singleton"))
184+
assertThat(context.getBeanDefinition("descriptionBean"))
193185
.isInstanceOfSatisfying(RootBeanDefinition.class, this::isTheValueField);
194186
}
195187

196188

197189
private void isTheValueField(RootBeanDefinition def) {
198190
assertThat(def.getQualifiedElement()).isInstanceOfSatisfying(Field.class, field -> {
199-
assertThat(field.getDeclaringClass()).isEqualTo(QualifiedBean.class);
191+
assertThat(field.getDeclaringClass()).isEqualTo(CaseByNameWithQualifier.class);
200192
assertThat(field.getName()).as("annotated field name")
201-
.isEqualTo("value");
193+
.isEqualTo("description");
202194
});
203195
}
204196

@@ -210,99 +202,67 @@ private AnnotationConfigApplicationContext createContext(Class<?> testClass) {
210202
}
211203

212204

213-
/*
214-
Classes to parse and register with the bean post processor
215-
-----
216-
Note that some of these are both a @Configuration class and bean override field holder.
217-
This is for this test convenience, as typically the bean override annotated fields
218-
should not be in configuration classes but rather in test case classes
219-
(where a TestExecutionListener automatically discovers and parses them).
220-
*/
205+
static class CaseByName {
221206

222-
static final SomeInterface OVERRIDE = new SomeImplementation();
207+
@DummyBean(beanName = "descriptionBean")
208+
private String description;
223209

224-
static final ExampleService OVERRIDE_SERVICE = new FailingExampleService();
225-
226-
static class ReplaceBeans {
210+
}
227211

228-
@ExampleBeanOverrideAnnotation(value = "useThis", beanName = "explicit")
229-
private ExampleService explicitName;
212+
static class CaseByType {
230213

231-
@ExampleBeanOverrideAnnotation(value = "useThis")
232-
private ExampleService implicitName;
214+
@DummyBean
215+
private Integer counter;
233216

234-
static ExampleService useThis() {
235-
return OVERRIDE_SERVICE;
236-
}
237217
}
238218

239-
static class CreateIfOriginalIsMissingBean {
219+
static class CaseByNameAndByType {
240220

241-
@ExampleBeanOverrideAnnotation(value = "useThis", createIfMissing = true, beanName = "explicit")
242-
private ExampleService explicitName;
221+
@DummyBean(beanName = "descriptionBean")
222+
private String description;
243223

244-
@ExampleBeanOverrideAnnotation(value = "useThis", createIfMissing = true)
245-
private ExampleService implicitName;
224+
@DummyBean
225+
private Integer counter;
246226

247-
static ExampleService useThis() {
248-
return OVERRIDE_SERVICE;
249-
}
250227
}
251228

252-
@Configuration(proxyBeanMethods = false)
253-
static class OverriddenFactoryBean {
229+
static class CaseByTypeWithReplaceOrCreateStrategy {
254230

255-
@ExampleBeanOverrideAnnotation(value = "fOverride", beanName = "beanToBeOverridden")
256-
SomeInterface f;
257-
258-
static SomeInterface fOverride() {
259-
return OVERRIDE;
260-
}
231+
@DummyBean(strategy = BeanOverrideStrategy.REPLACE_OR_CREATE_DEFINITION)
232+
private String description;
261233

262-
@Bean
263-
TestFactoryBean testFactoryBean() {
264-
return new TestFactoryBean();
265-
}
266234
}
267235

268-
static class EagerInitBean {
236+
static class CaseByNameAndByTypeWithReplaceOrCreateStrategy {
269237

270-
@ExampleBeanOverrideAnnotation(value = "useThis", createIfMissing = true)
271-
private ExampleService service;
238+
@DummyBean(beanName = "descriptionBean", strategy = BeanOverrideStrategy.REPLACE_OR_CREATE_DEFINITION)
239+
private String description;
240+
241+
@DummyBean(strategy = BeanOverrideStrategy.REPLACE_OR_CREATE_DEFINITION)
242+
private Integer counter;
272243

273-
static ExampleService useThis() {
274-
return OVERRIDE_SERVICE;
275-
}
276244
}
277245

278-
static class SingletonBean {
246+
static class CaseOverrideBeanProducedByFactoryBean {
279247

280-
@ExampleBeanOverrideAnnotation(beanName = "singleton",
281-
value = "useThis", createIfMissing = false)
282-
private String value;
248+
@DummyBean(beanName = "beanToBeOverridden")
249+
CharSequence description;
283250

284-
static String useThis() {
285-
return "USED THIS";
286-
}
287251
}
288252

289-
static class QualifiedBean {
253+
static class CaseByNameWithQualifier {
290254

291255
@Qualifier("preferThis")
292-
@ExampleBeanOverrideAnnotation(beanName = "singleton",
293-
value = "useThis", createIfMissing = false)
294-
private String value;
256+
@DummyBean(beanName = "descriptionBean")
257+
private String description;
295258

296-
static String useThis() {
297-
return "USED THIS";
298-
}
299259
}
300260

301261
static class TestFactoryBean implements FactoryBean<Object> {
302262

303263
@Override
304264
public Object getObject() {
305-
return new SomeImplementation();
265+
return "test";
306266
}
307267

308268
@Override
@@ -341,10 +301,4 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
341301
}
342302
}
343303

344-
interface SomeInterface {
345-
}
346-
347-
static class SomeImplementation implements SomeInterface {
348-
}
349-
350304
}

0 commit comments

Comments
 (0)