diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java index 1a01295a17ee..fa8aefec263f 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java @@ -59,6 +59,7 @@ * @author Simon Baslé * @author Stephane Nicoll * @author Sam Brannen + * @author Yanming Zhou * @since 6.2 */ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered { @@ -67,6 +68,9 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor, private static final BeanNameGenerator beanNameGenerator = DefaultBeanNameGenerator.INSTANCE; + private static final String unableToOverrideByTypeDiagnosticsMessage = " If the bean is defined from a @Bean method," + + " please make sure the return type is the most specific type (recommended) or type can be assigned to %s"; + private final Set beanOverrideHandlers; private final BeanOverrideRegistry beanOverrideRegistry; @@ -170,7 +174,7 @@ else if (requireExistingBean) { Field field = handler.getField(); throw new IllegalStateException( "Unable to replace bean: there is no bean with name '%s' and type %s%s." - .formatted(beanName, handler.getBeanType(), requiredByField(field))); + .formatted(beanName, handler.getBeanType(), requiredByField(field, handler.getBeanType()))); } // 4) We are creating a bean by-name with the provided beanName. } @@ -257,7 +261,7 @@ private void wrapBean(ConfigurableListableBeanFactory beanFactory, BeanOverrideH String message = "Unable to select a bean to wrap: "; int candidateCount = candidateNames.size(); if (candidateCount == 0) { - message += "there are no beans of type %s%s.".formatted(beanType, requiredByField(field)); + message += "there are no beans of type %s%s.".formatted(beanType, requiredByField(field, beanType)); } else { message += "found %d beans of type %s%s: %s" @@ -299,7 +303,7 @@ private static String getBeanNameForType(ConfigurableListableBeanFactory beanFac if (requireExistingBean) { throw new IllegalStateException( "Unable to override bean: there are no beans of type %s%s." - .formatted(beanType, requiredByField(field))); + .formatted(beanType, requiredByField(field, beanType))); } return null; } @@ -483,4 +487,8 @@ private static String requiredByField(@Nullable Field field) { field.getDeclaringClass().getSimpleName(), field.getName()); } + private static String requiredByField(@Nullable Field field, ResolvableType requiredBeanType) { + return requiredByField(field) + '.' + unableToOverrideByTypeDiagnosticsMessage.formatted(requiredBeanType); + } + } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessorTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessorTests.java index 8b3ce3c1a6b0..affcd21db70a 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessorTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessorTests.java @@ -85,7 +85,7 @@ void replaceBeanByNameWithoutMatchingBeanDefinitionFails() { assertThatIllegalStateException() .isThrownBy(context::refresh) - .withMessage(""" + .withMessageStartingWith(""" Unable to replace bean: there is no bean with name 'descriptionBean' \ and type java.lang.String (as required by field 'ByNameTestCase.description')."""); } @@ -97,7 +97,7 @@ void replaceBeanByNameWithMatchingBeanDefinitionAndWrongTypeFails() { assertThatIllegalStateException() .isThrownBy(context::refresh) - .withMessage(""" + .withMessageStartingWith(""" Unable to replace bean: there is no bean with name 'descriptionBean' \ and type java.lang.String (as required by field 'ByNameTestCase.description')."""); } @@ -144,7 +144,7 @@ void replaceBeanByTypeWithoutMatchingBeanFails() { assertThatIllegalStateException() .isThrownBy(context::refresh) - .withMessage(""" + .withMessageStartingWith(""" Unable to override bean: there are no beans of type java.lang.Integer \ (as required by field 'ByTypeTestCase.counter')."""); } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanTests.java index d771af6333ee..a036872775b2 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanTests.java @@ -40,7 +40,7 @@ void cannotOverrideBeanByNameWithNoSuchBeanName() { BeanOverrideContextCustomizerTestUtils.customizeApplicationContext(FailureByNameLookup.class, context); assertThatIllegalStateException() .isThrownBy(context::refresh) - .withMessage(""" + .withMessageStartingWith(""" Unable to replace bean: there is no bean with name 'beanToOverride' \ and type java.lang.String (as required by field 'FailureByNameLookup.example')."""); } @@ -52,7 +52,7 @@ void cannotOverrideBeanByNameWithBeanOfWrongType() { BeanOverrideContextCustomizerTestUtils.customizeApplicationContext(FailureByNameLookup.class, context); assertThatIllegalStateException() .isThrownBy(context::refresh) - .withMessage(""" + .withMessageStartingWith(""" Unable to replace bean: there is no bean with name 'beanToOverride' \ and type java.lang.String (as required by field 'FailureByNameLookup.example')."""); } @@ -63,7 +63,7 @@ void cannotOverrideBeanByTypeWithNoSuchBeanType() { BeanOverrideContextCustomizerTestUtils.customizeApplicationContext(FailureByTypeLookup.class, context); assertThatIllegalStateException() .isThrownBy(context::refresh) - .withMessage(""" + .withMessageStartingWith(""" Unable to override bean: there are no beans of \ type %s (as required by field '%s.example').""", String.class.getName(), FailureByTypeLookup.class.getSimpleName()); diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanConfigurationErrorTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanConfigurationErrorTests.java index 1182a8afc59c..07a06927b41f 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanConfigurationErrorTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanConfigurationErrorTests.java @@ -40,7 +40,7 @@ void cannotOverrideBeanByNameWithNoSuchBeanName() { BeanOverrideContextCustomizerTestUtils.customizeApplicationContext(FailureByNameLookup.class, context); assertThatIllegalStateException() .isThrownBy(context::refresh) - .withMessage(""" + .withMessageStartingWith(""" Unable to replace bean: there is no bean with name 'beanToOverride' \ and type java.lang.String (as required by field 'FailureByNameLookup.example')."""); } @@ -52,7 +52,7 @@ void cannotOverrideBeanByNameWithBeanOfWrongType() { BeanOverrideContextCustomizerTestUtils.customizeApplicationContext(FailureByNameLookup.class, context); assertThatIllegalStateException() .isThrownBy(context::refresh) - .withMessage(""" + .withMessageStartingWith(""" Unable to replace bean: there is no bean with name 'beanToOverride' \ and type java.lang.String (as required by field 'FailureByNameLookup.example')."""); } @@ -63,7 +63,7 @@ void cannotOverrideBeanByTypeWithNoSuchBeanType() { BeanOverrideContextCustomizerTestUtils.customizeApplicationContext(FailureByTypeLookup.class, context); assertThatIllegalStateException() .isThrownBy(context::refresh) - .withMessage(""" + .withMessageStartingWith(""" Unable to override bean: there are no beans of \ type java.lang.String (as required by field 'FailureByTypeLookup.example')."""); } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanConfigurationErrorTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanConfigurationErrorTests.java index efdbf8873334..23b42908a7ff 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanConfigurationErrorTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanConfigurationErrorTests.java @@ -50,7 +50,7 @@ void contextCustomizerCannotBeCreatedWithNoSuchBeanType() { BeanOverrideContextCustomizerTestUtils.customizeApplicationContext(ByTypeSingleLookup.class, context); assertThatIllegalStateException() .isThrownBy(context::refresh) - .withMessage(""" + .withMessageStartingWith(""" Unable to select a bean to wrap: there are no beans of type java.lang.String \ (as required by field 'ByTypeSingleLookup.example')."""); }