Ensure @BeanOverride in subclass takes precedence over superclass
Prior to this commit, a @BeanOverride (such as @TestBean) for a specific target bean which was declared in a superclass always took precedence over a bean override for the same target bean in a subclass, thereby rendering the bean override configuration in the subclass useless. In other words, there was no way for a test class to override a bean override declared in a superclass. To address that, this commit switches from direct use of ReflectionUtils.doWithFields() to a custom search algorithm that traverses the class hierarchy using tail recursion for processing @BeanOverride fields (delegating now to ReflectionUtils.doWithLocalFields() in order to continue to benefit from the caching of declared fields in ReflectionUtils). Closes gh-34194
This commit is contained in:
@@ -38,14 +38,21 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Sam Brannen
|
||||
* @since 6.2
|
||||
*/
|
||||
public class TestBeanForInheritanceIntegrationTests {
|
||||
@SpringJUnitConfig
|
||||
public class TestBeanInheritanceIntegrationTests {
|
||||
|
||||
@TestBean
|
||||
Pojo puzzleBean;
|
||||
|
||||
static Pojo puzzleBean() {
|
||||
return new FakePojo("puzzle in enclosing class");
|
||||
}
|
||||
|
||||
static Pojo enclosingClassBean() {
|
||||
return new FakePojo("in enclosing test class");
|
||||
}
|
||||
|
||||
@SpringJUnitConfig
|
||||
abstract static class AbstractTestBeanIntegrationTestCase {
|
||||
abstract static class AbstractTestCase {
|
||||
|
||||
@TestBean
|
||||
Pojo someBean;
|
||||
@@ -56,6 +63,9 @@ public class TestBeanForInheritanceIntegrationTests {
|
||||
@TestBean("thirdBean")
|
||||
Pojo anotherBean;
|
||||
|
||||
@TestBean
|
||||
Pojo enigmaBean;
|
||||
|
||||
static Pojo otherBean() {
|
||||
return new FakePojo("other in superclass");
|
||||
}
|
||||
@@ -64,44 +74,18 @@ public class TestBeanForInheritanceIntegrationTests {
|
||||
return new FakePojo("third in superclass");
|
||||
}
|
||||
|
||||
static Pojo enigmaBean() {
|
||||
return new FakePojo("enigma in superclass");
|
||||
}
|
||||
|
||||
static Pojo commonBean() {
|
||||
return new FakePojo("common in superclass");
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
Pojo someBean() {
|
||||
return new ProdPojo();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Pojo otherBean() {
|
||||
return new ProdPojo();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Pojo thirdBean() {
|
||||
return new ProdPojo();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Pojo pojo() {
|
||||
return new ProdPojo();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Pojo pojo2() {
|
||||
return new ProdPojo();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Nested, concrete inherited tests with correct @TestBean setup")
|
||||
class NestedConcreteTestBeanIntegrationTests extends AbstractTestBeanIntegrationTestCase {
|
||||
class NestedTests extends AbstractTestCase {
|
||||
|
||||
@Autowired
|
||||
ApplicationContext ctx;
|
||||
@@ -112,6 +96,21 @@ public class TestBeanForInheritanceIntegrationTests {
|
||||
@TestBean(name = "pojo2", methodName = "enclosingClassBean")
|
||||
Pojo pojo2;
|
||||
|
||||
@TestBean(methodName = "localEnigmaBean")
|
||||
Pojo enigmaBean;
|
||||
|
||||
@TestBean
|
||||
Pojo puzzleBean;
|
||||
|
||||
|
||||
static Pojo puzzleBean() {
|
||||
return new FakePojo("puzzle in nested class");
|
||||
}
|
||||
|
||||
static Pojo localEnigmaBean() {
|
||||
return new FakePojo("enigma in subclass");
|
||||
}
|
||||
|
||||
static Pojo someBean() {
|
||||
return new FakePojo("someBeanOverride");
|
||||
}
|
||||
@@ -150,6 +149,57 @@ public class TestBeanForInheritanceIntegrationTests {
|
||||
assertThat(ctx.getBean("pojo2")).as("applicationContext").hasToString("in enclosing test class");
|
||||
assertThat(this.pojo2.value()).as("injection point").isEqualTo("in enclosing test class");
|
||||
}
|
||||
|
||||
@Test // gh-34194
|
||||
void testBeanInSubclassOverridesTestBeanInSuperclass() {
|
||||
assertThat(ctx.getBean("enigmaBean")).as("applicationContext").hasToString("enigma in subclass");
|
||||
assertThat(this.enigmaBean.value()).as("injection point").isEqualTo("enigma in subclass");
|
||||
}
|
||||
|
||||
@Test // gh-34194
|
||||
void testBeanInNestedClassOverridesTestBeanInEnclosingClass() {
|
||||
assertThat(ctx.getBean("puzzleBean")).as("applicationContext").hasToString("puzzle in nested class");
|
||||
assertThat(this.puzzleBean.value()).as("injection point").isEqualTo("puzzle in nested class");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
Pojo someBean() {
|
||||
return new ProdPojo();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Pojo otherBean() {
|
||||
return new ProdPojo();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Pojo thirdBean() {
|
||||
return new ProdPojo();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Pojo enigmaBean() {
|
||||
return new ProdPojo();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Pojo puzzleBean() {
|
||||
return new ProdPojo();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Pojo pojo() {
|
||||
return new ProdPojo();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Pojo pojo2() {
|
||||
return new ProdPojo();
|
||||
}
|
||||
}
|
||||
|
||||
interface Pojo {
|
||||
Reference in New Issue
Block a user