Construct consistent error messages in BeanOverrideBeanFactoryPostProcessor

This commit is contained in:
Sam Brannen
2024-11-21 11:15:02 +01:00
parent 08a789cee9
commit b9cf03f8f0
5 changed files with 64 additions and 56 deletions

View File

@@ -124,6 +124,7 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
// 3) AOT runtime // 3) AOT runtime
String beanName = handler.getBeanName(); String beanName = handler.getBeanName();
Field field = handler.getField();
BeanDefinition existingBeanDefinition = null; BeanDefinition existingBeanDefinition = null;
if (beanName == null) { if (beanName == null) {
beanName = getBeanNameForType(beanFactory, handler, requireExistingBean); beanName = getBeanNameForType(beanFactory, handler, requireExistingBean);
@@ -150,9 +151,10 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
} }
else if (requireExistingBean) { else if (requireExistingBean) {
throw new IllegalStateException(""" throw new IllegalStateException("""
Unable to override bean: there is no bean to replace \ Unable to replace bean: there is no bean with name '%s' and type %s \
with name [%s] and type [%s].""" (as required by field '%s.%s')."""
.formatted(beanName, handler.getBeanType())); .formatted(beanName, handler.getBeanType(),
field.getDeclaringClass().getSimpleName(), field.getName()));
} }
} }
@@ -179,7 +181,7 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
if (!(beanFactory instanceof BeanDefinitionRegistry registry)) { if (!(beanFactory instanceof BeanDefinitionRegistry registry)) {
throw new IllegalStateException("Cannot process bean override with a BeanFactory " + throw new IllegalStateException("Cannot process bean override with a BeanFactory " +
"that doesn't implement BeanDefinitionRegistry: " + beanFactory.getClass().getName()); "that does not implement BeanDefinitionRegistry: " + beanFactory.getClass().getName());
} }
RootBeanDefinition pseudoBeanDefinition = createPseudoBeanDefinition(handler); RootBeanDefinition pseudoBeanDefinition = createPseudoBeanDefinition(handler);
@@ -220,6 +222,7 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
*/ */
private void wrapBean(ConfigurableListableBeanFactory beanFactory, BeanOverrideHandler handler) { private void wrapBean(ConfigurableListableBeanFactory beanFactory, BeanOverrideHandler handler) {
String beanName = handler.getBeanName(); String beanName = handler.getBeanName();
Field field = handler.getField();
ResolvableType beanType = handler.getBeanType(); ResolvableType beanType = handler.getBeanType();
if (beanName == null) { if (beanName == null) {
@@ -235,13 +238,17 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
beanName = primaryCandidate; beanName = primaryCandidate;
} }
else { else {
Field field = handler.getField(); String message = "Unable to select a bean to wrap: ";
throw new IllegalStateException(""" if (candidateCount == 0) {
Unable to select a bean to override by wrapping: found %d bean instances of type %s \ message += "there are no beans of type %s (as required by field '%s.%s')."
(as required by annotated field '%s.%s')%s""" .formatted(beanType, field.getDeclaringClass().getSimpleName(), field.getName());
}
else {
message += "found %d beans of type %s (as required by field '%s.%s'): %s"
.formatted(candidateCount, beanType, field.getDeclaringClass().getSimpleName(), .formatted(candidateCount, beanType, field.getDeclaringClass().getSimpleName(),
field.getName(), (candidateCount > 0 ? ": " + candidateNames : ""))); field.getName(), candidateNames);
}
throw new IllegalStateException(message);
} }
} }
beanName = BeanFactoryUtils.transformedBeanName(beanName); beanName = BeanFactoryUtils.transformedBeanName(beanName);
@@ -251,9 +258,10 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
Set<String> candidates = getExistingBeanNamesByType(beanFactory, handler, false); Set<String> candidates = getExistingBeanNamesByType(beanFactory, handler, false);
if (!candidates.contains(beanName)) { if (!candidates.contains(beanName)) {
throw new IllegalStateException(""" throw new IllegalStateException("""
Unable to override bean by wrapping: there is no existing bean \ Unable to wrap bean: there is no bean with name '%s' and type %s \
with name [%s] and type [%s].""" (as required by field '%s.%s')."""
.formatted(beanName, beanType)); .formatted(beanName, beanType, field.getDeclaringClass().getSimpleName(),
field.getName()));
} }
} }
@@ -276,7 +284,7 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
else if (candidateCount == 0) { else if (candidateCount == 0) {
if (requireExistingBean) { if (requireExistingBean) {
throw new IllegalStateException( throw new IllegalStateException(
"Unable to override bean: no beans of type %s (as required by annotated field '%s.%s')" "Unable to override bean: there are no beans of type %s (as required by field '%s.%s')."
.formatted(beanType, field.getDeclaringClass().getSimpleName(), field.getName())); .formatted(beanType, field.getDeclaringClass().getSimpleName(), field.getName()));
} }
return null; return null;
@@ -287,9 +295,8 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
return primaryCandidate; return primaryCandidate;
} }
throw new IllegalStateException(""" throw new IllegalStateException(
Unable to select a bean to override: found %s beans of type %s \ "Unable to select a bean to override: found %d beans of type %s (as required by field '%s.%s'): %s"
(as required by annotated field '%s.%s'): %s"""
.formatted(candidateCount, beanType, field.getDeclaringClass().getSimpleName(), .formatted(candidateCount, beanType, field.getDeclaringClass().getSimpleName(),
field.getName(), candidateNames)); field.getName(), candidateNames));
} }
@@ -416,7 +423,7 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
private static void destroySingleton(ConfigurableListableBeanFactory beanFactory, String beanName) { private static void destroySingleton(ConfigurableListableBeanFactory beanFactory, String beanName) {
if (!(beanFactory instanceof DefaultListableBeanFactory dlbf)) { if (!(beanFactory instanceof DefaultListableBeanFactory dlbf)) {
throw new IllegalStateException("Cannot process bean override with a BeanFactory " + throw new IllegalStateException("Cannot process bean override with a BeanFactory " +
"that doesn't implement DefaultListableBeanFactory: " + beanFactory.getClass().getName()); "that does not implement DefaultListableBeanFactory: " + beanFactory.getClass().getName());
} }
dlbf.destroySingleton(beanName); dlbf.destroySingleton(beanName);
} }

View File

@@ -18,6 +18,7 @@ package org.springframework.test.context.bean.override;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate; import java.util.function.Predicate;
@@ -84,8 +85,9 @@ class BeanOverrideBeanFactoryPostProcessorTests {
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(context::refresh) .isThrownBy(context::refresh)
.withMessage("Unable to override bean: there is no bean " + .withMessage("""
"to replace with name [descriptionBean] and type [java.lang.String]."); Unable to replace bean: there is no bean with name 'descriptionBean' \
and type java.lang.String (as required by field 'ByNameTestCase.description').""");
} }
@Test @Test
@@ -95,8 +97,9 @@ class BeanOverrideBeanFactoryPostProcessorTests {
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(context::refresh) .isThrownBy(context::refresh)
.withMessage("Unable to override bean: there is no bean " + .withMessage("""
"to replace with name [descriptionBean] and type [java.lang.String]."); Unable to replace bean: there is no bean with name 'descriptionBean' \
and type java.lang.String (as required by field 'ByNameTestCase.description').""");
} }
@Test @Test
@@ -141,8 +144,9 @@ class BeanOverrideBeanFactoryPostProcessorTests {
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(context::refresh) .isThrownBy(context::refresh)
.withMessage("Unable to override bean: no beans of type java.lang.Integer " + .withMessage("""
"(as required by annotated field 'ByTypeTestCase.counter')"); Unable to override bean: there are no beans of type java.lang.Integer \
(as required by field 'ByTypeTestCase.counter').""");
} }
@Test @Test
@@ -153,9 +157,10 @@ class BeanOverrideBeanFactoryPostProcessorTests {
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(context::refresh) .isThrownBy(context::refresh)
.withMessage("Unable to select a bean to override: found 2 beans " + .withMessage("""
"of type java.lang.Integer (as required by annotated field 'ByTypeTestCase.counter'): " + Unable to select a bean to override: found 2 beans of type java.lang.Integer \
"[someInteger, anotherInteger]"); (as required by field 'ByTypeTestCase.counter'): %s""",
List.of("someInteger", "anotherInteger"));
} }
@Test @Test

View File

@@ -40,8 +40,8 @@ public class TestBeanTests {
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(context::refresh) .isThrownBy(context::refresh)
.withMessage(""" .withMessage("""
Unable to override bean: there is no bean \ Unable to replace bean: there is no bean with name 'beanToOverride' \
to replace with name [beanToOverride] and type [java.lang.String]."""); and type java.lang.String (as required by field 'FailureByNameLookup.example').""");
} }
@Test @Test
@@ -52,8 +52,8 @@ public class TestBeanTests {
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(context::refresh) .isThrownBy(context::refresh)
.withMessage(""" .withMessage("""
Unable to override bean: there is no bean \ Unable to replace bean: there is no bean with name 'beanToOverride' \
to replace with name [beanToOverride] and type [java.lang.String]."""); and type java.lang.String (as required by field 'FailureByNameLookup.example').""");
} }
@Test @Test
@@ -63,8 +63,8 @@ public class TestBeanTests {
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(context::refresh) .isThrownBy(context::refresh)
.withMessage(""" .withMessage("""
Unable to override bean: no beans of \ Unable to override bean: there are no beans of \
type %s (as required by annotated field '%s.example')""".formatted( type %s (as required by field '%s.example').""".formatted(
String.class.getName(), FailureByTypeLookup.class.getSimpleName())); String.class.getName(), FailureByTypeLookup.class.getSimpleName()));
} }
@@ -77,9 +77,8 @@ public class TestBeanTests {
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(context::refresh) .isThrownBy(context::refresh)
.withMessage(""" .withMessage("""
Unable to select a bean to override: found 2 beans \ Unable to select a bean to override: found 2 beans of type java.lang.String \
of type %s (as required by annotated field '%s.example'): %s""".formatted( (as required by field 'FailureByTypeLookup.example'): %s""", List.of("bean1", "bean2"));
String.class.getName(), FailureByTypeLookup.class.getSimpleName(), List.of("bean1", "bean2")));
} }
@Test @Test

View File

@@ -41,8 +41,8 @@ class MockitoBeanConfigurationErrorTests {
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(context::refresh) .isThrownBy(context::refresh)
.withMessage(""" .withMessage("""
Unable to override bean: there is no bean \ Unable to replace bean: there is no bean with name 'beanToOverride' \
to replace with name [beanToOverride] and type [java.lang.String]."""); and type java.lang.String (as required by field 'FailureByNameLookup.example').""");
} }
@Test @Test
@@ -53,8 +53,8 @@ class MockitoBeanConfigurationErrorTests {
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(context::refresh) .isThrownBy(context::refresh)
.withMessage(""" .withMessage("""
Unable to override bean: there is no bean \ Unable to replace bean: there is no bean with name 'beanToOverride' \
to replace with name [beanToOverride] and type [java.lang.String]."""); and type java.lang.String (as required by field 'FailureByNameLookup.example').""");
} }
@Test @Test
@@ -64,9 +64,8 @@ class MockitoBeanConfigurationErrorTests {
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(context::refresh) .isThrownBy(context::refresh)
.withMessage(""" .withMessage("""
Unable to override bean: no beans of \ Unable to override bean: there are no beans of \
type %s (as required by annotated field '%s.example')""".formatted( type java.lang.String (as required by field 'FailureByTypeLookup.example').""");
String.class.getName(), FailureByTypeLookup.class.getSimpleName()));
} }
@Test @Test
@@ -78,9 +77,9 @@ class MockitoBeanConfigurationErrorTests {
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(context::refresh) .isThrownBy(context::refresh)
.withMessage(""" .withMessage("""
Unable to select a bean to override: found 2 beans \ Unable to select a bean to override: found 2 beans of type java.lang.String \
of type %s (as required by annotated field '%s.example'): %s""".formatted( (as required by field 'FailureByTypeLookup.example'): %s""",
String.class.getName(), FailureByTypeLookup.class.getSimpleName(), List.of("bean1", "bean2"))); List.of("bean1", "bean2"));
} }

View File

@@ -40,10 +40,9 @@ class MockitoSpyBeanConfigurationErrorTests {
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(context::refresh) .isThrownBy(context::refresh)
.withMessage(""" .withMessage("""
Unable to override bean by wrapping: \ Unable to wrap bean: there is no bean with name 'beanToSpy' and \
there is no existing bean with name [beanToSpy] and type [%s].""", type java.lang.String (as required by field 'ByNameSingleLookup.example').""");
String.class.getName()); }
}
@Test @Test
void contextCustomizerCannotBeCreatedWithNoSuchBeanType() { void contextCustomizerCannotBeCreatedWithNoSuchBeanType() {
@@ -52,9 +51,8 @@ class MockitoSpyBeanConfigurationErrorTests {
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(context::refresh) .isThrownBy(context::refresh)
.withMessage(""" .withMessage("""
Unable to select a bean to override by wrapping: found 0 bean instances of \ Unable to select a bean to wrap: there are no beans of type java.lang.String \
type %s (as required by annotated field '%s.example')""", (as required by field 'ByTypeSingleLookup.example').""");
String.class.getName(), ByTypeSingleLookup.class.getSimpleName());
} }
@Test @Test
@@ -66,9 +64,9 @@ class MockitoSpyBeanConfigurationErrorTests {
assertThatIllegalStateException() assertThatIllegalStateException()
.isThrownBy(context::refresh) .isThrownBy(context::refresh)
.withMessage(""" .withMessage("""
Unable to select a bean to override by wrapping: found 2 bean instances \ Unable to select a bean to wrap: found 2 beans of type java.lang.String \
of type %s (as required by annotated field '%s.example'): %s""", (as required by field 'ByTypeSingleLookup.example'): %s""",
String.class.getName(), ByTypeSingleLookup.class.getSimpleName(), List.of("bean1", "bean2")); List.of("bean1", "bean2"));
} }