Properly resolve @TestBean factory method within class hierarchy
Prior to this commit, the search algorithm used to locate a @TestBean factory method within a test class hierarchy incorrectly found factory methods declared in subclasses or nested test classes "below" the class in which the @TestBean field was declared. This resulted in "duplicate bean override" failures for @TestBean overrides which are clearly not duplicates but rather "overrides of an override". This commit ensures that @TestBean factory method resolution is consistent in type hierarchies as well as in enclosing class hierarchies (for @Nested test classes) by beginning the search for a factory method in the class which declares the @TestBean field. Closes gh-34204
This commit is contained in:
@@ -48,19 +48,16 @@ public class TestBeanInheritanceIntegrationTests {
|
||||
return new FakePojo("puzzle in enclosing class");
|
||||
}
|
||||
|
||||
static Pojo enclosingClassBean() {
|
||||
static Pojo enclosingClassFactoryMethod() {
|
||||
return new FakePojo("in enclosing test class");
|
||||
}
|
||||
|
||||
abstract static class AbstractTestCase {
|
||||
|
||||
@TestBean
|
||||
Pojo someBean;
|
||||
|
||||
@TestBean("otherBean")
|
||||
Pojo otherBean;
|
||||
|
||||
@TestBean("thirdBean")
|
||||
@TestBean
|
||||
Pojo anotherBean;
|
||||
|
||||
@TestBean
|
||||
@@ -70,8 +67,8 @@ public class TestBeanInheritanceIntegrationTests {
|
||||
return new FakePojo("other in superclass");
|
||||
}
|
||||
|
||||
static Pojo thirdBean() {
|
||||
return new FakePojo("third in superclass");
|
||||
static Pojo anotherBean() {
|
||||
return new FakePojo("another in superclass");
|
||||
}
|
||||
|
||||
static Pojo enigmaBean() {
|
||||
@@ -93,49 +90,42 @@ public class TestBeanInheritanceIntegrationTests {
|
||||
@TestBean(methodName = "commonBean")
|
||||
Pojo pojo;
|
||||
|
||||
@TestBean(name = "pojo2", methodName = "enclosingClassBean")
|
||||
@TestBean(name = "pojo2", methodName = "enclosingClassFactoryMethod")
|
||||
Pojo pojo2;
|
||||
|
||||
@TestBean(methodName = "localEnigmaBean")
|
||||
@TestBean
|
||||
Pojo enigmaBean;
|
||||
|
||||
@TestBean
|
||||
Pojo puzzleBean;
|
||||
|
||||
|
||||
// "Overrides" puzzleBean() defined in TestBeanInheritanceIntegrationTests.
|
||||
static Pojo puzzleBean() {
|
||||
return new FakePojo("puzzle in nested class");
|
||||
}
|
||||
|
||||
static Pojo localEnigmaBean() {
|
||||
// "Overrides" enigmaBean() defined in AbstractTestCase.
|
||||
static Pojo enigmaBean() {
|
||||
return new FakePojo("enigma in subclass");
|
||||
}
|
||||
|
||||
static Pojo someBean() {
|
||||
return new FakePojo("someBeanOverride");
|
||||
}
|
||||
|
||||
// "Overrides" otherBean() defined in AbstractTestBeanIntegrationTestCase.
|
||||
static Pojo otherBean() {
|
||||
return new FakePojo("other in subclass");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fieldInSuperclassWithFactoryMethodInSuperclass() {
|
||||
assertThat(ctx.getBean("thirdBean")).as("applicationContext").hasToString("third in superclass");
|
||||
assertThat(super.anotherBean.value()).as("injection point").isEqualTo("third in superclass");
|
||||
assertThat(ctx.getBean("anotherBean")).as("applicationContext").hasToString("another in superclass");
|
||||
assertThat(super.anotherBean.value()).as("injection point").isEqualTo("another in superclass");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fieldInSuperclassWithFactoryMethodInSubclass() {
|
||||
assertThat(ctx.getBean("someBean")).as("applicationContext").hasToString("someBeanOverride");
|
||||
assertThat(super.someBean.value()).as("injection point").isEqualTo("someBeanOverride");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fieldInSuperclassWithFactoryMethodInSupeclassAndInSubclass() {
|
||||
assertThat(ctx.getBean("otherBean")).as("applicationContext").hasToString("other in subclass");
|
||||
assertThat(super.otherBean.value()).as("injection point").isEqualTo("other in subclass");
|
||||
@Test // gh-34204
|
||||
void fieldInSuperclassWithFactoryMethodInSuperclassAndInSubclass() {
|
||||
// We do not expect "other in subclass", because the @TestBean declaration in
|
||||
// AbstractTestCase cannot "see" the otherBean() factory method in the subclass.
|
||||
assertThat(ctx.getBean("otherBean")).as("applicationContext").hasToString("other in superclass");
|
||||
assertThat(super.otherBean.value()).as("injection point").isEqualTo("other in superclass");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -150,13 +140,13 @@ public class TestBeanInheritanceIntegrationTests {
|
||||
assertThat(this.pojo2.value()).as("injection point").isEqualTo("in enclosing test class");
|
||||
}
|
||||
|
||||
@Test // gh-34194
|
||||
@Test // gh-34194, gh-34204
|
||||
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
|
||||
@Test // gh-34194, gh-34204
|
||||
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");
|
||||
@@ -166,18 +156,13 @@ public class TestBeanInheritanceIntegrationTests {
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
Pojo someBean() {
|
||||
return new ProdPojo();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Pojo otherBean() {
|
||||
return new ProdPojo();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Pojo thirdBean() {
|
||||
Pojo anotherBean() {
|
||||
return new ProdPojo();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,9 +26,10 @@ import org.springframework.test.context.bean.override.BeanOverrideContextCustomi
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link TestBean}.
|
||||
* Tests for {@link TestBean @TestBean}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class TestBeanTests {
|
||||
|
||||
@@ -109,7 +110,7 @@ public class TestBeanTests {
|
||||
.isThrownBy(() -> BeanOverrideContextCustomizerTestUtils.customizeApplicationContext(
|
||||
FailureOverrideInParentWithoutFactoryMethod.class, context))
|
||||
.withMessage("No static method found named beanToOverride() in %s with return type %s",
|
||||
FailureOverrideInParentWithoutFactoryMethod.class.getName(), String.class.getName());
|
||||
AbstractByNameLookup.class.getName(), String.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -149,8 +150,7 @@ public class TestBeanTests {
|
||||
@TestBean(name = "beanToOverride")
|
||||
private String example;
|
||||
|
||||
// Expected static String example() { ... }
|
||||
// or static String beanToOverride() { ... }
|
||||
// No example() or beanToOverride() method
|
||||
}
|
||||
|
||||
static class FailureMissingExplicitOverrideMethod {
|
||||
@@ -158,24 +158,21 @@ public class TestBeanTests {
|
||||
@TestBean(methodName = "createExample")
|
||||
private String example;
|
||||
|
||||
// Expected static String createExample() { ... }
|
||||
// NO createExample() method
|
||||
}
|
||||
|
||||
abstract static class AbstractByNameLookup {
|
||||
|
||||
@TestBean(methodName = "beanToOverride")
|
||||
protected String beanToOverride;
|
||||
}
|
||||
|
||||
static class FailureOverrideInParentWithoutFactoryMethod extends AbstractByNameLookup {
|
||||
@TestBean
|
||||
String beanToOverride;
|
||||
|
||||
// No beanToOverride() method
|
||||
}
|
||||
|
||||
abstract static class AbstractCompetingMethods {
|
||||
static class FailureOverrideInParentWithoutFactoryMethod extends AbstractByNameLookup {
|
||||
}
|
||||
|
||||
@TestBean(name = "beanToOverride")
|
||||
protected String example;
|
||||
abstract static class AbstractCompetingMethods {
|
||||
|
||||
static String example() {
|
||||
throw new IllegalStateException("Should not be called");
|
||||
@@ -184,6 +181,9 @@ public class TestBeanTests {
|
||||
|
||||
static class FailureCompetingOverrideMethods extends AbstractCompetingMethods {
|
||||
|
||||
@TestBean(name = "beanToOverride")
|
||||
String example;
|
||||
|
||||
static String beanToOverride() {
|
||||
throw new IllegalStateException("Should not be called");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user