Browse Source

Improve exception handling in bean override support

- Do not silently abort bean override processing if the ApplicationContext
  is not a BeanDefinitionRegistry.

- Include conflicting bean names in error messages instead of just the
  number of conflicting beans.

- Consistently throw IllegalStateException.

- etc.
pull/32954/head
Sam Brannen 2 years ago
parent
commit
acdd8a0015
  1. 38
      spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java
  2. 6
      spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizer.java
  3. 7
      spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessor.java
  4. 12
      spring-test/src/test/java/org/springframework/test/context/bean/override/convention/FailingTestBeanByTypeIntegrationTests.java
  5. 12
      spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/FailingMockitoBeanByTypeIntegrationTests.java
  6. 10
      spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/FailingMockitoSpyBeanByTypeIntegrationTests.java
  7. 2
      spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/FailingMockitoSpyBeanIntegrationTests.java

38
spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessor.java

@ -130,15 +130,16 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor, @@ -130,15 +130,16 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
String beanName = overrideMetadata.getBeanName();
BeanDefinition existingBeanDefinition = null;
if (beanName == null) {
Set<String> candidates = getExistingBeanNamesByType(beanFactory, overrideMetadata, true);
if (candidates.size() != 1) {
Field f = overrideMetadata.getField();
throw new IllegalStateException("Unable to select a bean definition to override: " +
candidates.size() + " bean definitions found of type " + overrideMetadata.getBeanType() +
" (as required by annotated field '" + f.getDeclaringClass().getSimpleName() +
"." + f.getName() + "')");
Set<String> candidateNames = getExistingBeanNamesByType(beanFactory, overrideMetadata, true);
int candidateCount = candidateNames.size();
if (candidateCount != 1) {
Field field = overrideMetadata.getField();
throw new IllegalStateException("Unable to select a bean definition to override: found " +
candidateCount + " bean definitions of type " + overrideMetadata.getBeanType() +
" (as required by annotated field '" + field.getDeclaringClass().getSimpleName() +
"." + field.getName() + "')" + (candidateCount > 0 ? ": " + candidateNames : ""));
}
beanName = candidates.iterator().next();
beanName = candidateNames.iterator().next();
existingBeanDefinition = beanFactory.getBeanDefinition(beanName);
}
else {
@ -147,8 +148,8 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor, @@ -147,8 +148,8 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
existingBeanDefinition = beanFactory.getBeanDefinition(beanName);
}
else if (enforceExistingDefinition) {
throw new IllegalStateException("Unable to override bean '" + beanName + "': there is no" +
" bean definition to replace with that name of type " + overrideMetadata.getBeanType());
throw new IllegalStateException("Unable to override bean '" + beanName + "': there is no " +
"bean definition to replace with that name of type " + overrideMetadata.getBeanType());
}
}
@ -181,20 +182,21 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor, @@ -181,20 +182,21 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
String beanName = metadata.getBeanName();
if (beanName == null) {
Set<String> candidateNames = getExistingBeanNamesByType(beanFactory, metadata, true);
if (candidateNames.size() != 1) {
Field f = metadata.getField();
throw new IllegalStateException("Unable to select a bean to override by wrapping: " +
candidateNames.size() + " bean instances found of type " + metadata.getBeanType() +
" (as required by annotated field '" + f.getDeclaringClass().getSimpleName() +
"." + f.getName() + "')");
int candidateCount = candidateNames.size();
if (candidateCount != 1) {
Field field = metadata.getField();
throw new IllegalStateException("Unable to select a bean to override by wrapping: found " +
candidateCount + " bean instances of type " + metadata.getBeanType() +
" (as required by annotated field '" + field.getDeclaringClass().getSimpleName() +
"." + field.getName() + "')" + (candidateCount > 0 ? ": " + candidateNames : ""));
}
beanName = candidateNames.iterator().next();
}
else {
Set<String> candidates = getExistingBeanNamesByType(beanFactory, metadata, false);
if (!candidates.contains(beanName)) {
throw new IllegalStateException("Unable to override bean '" + beanName + "' by wrapping: there is no" +
" existing bean instance with that name of type " + metadata.getBeanType());
throw new IllegalStateException("Unable to override bean '" + beanName + "' by wrapping: there is no " +
"existing bean instance with that name of type " + metadata.getBeanType());
}
}
this.overrideRegistrar.markWrapEarly(metadata, beanName);

6
spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizer.java

@ -81,9 +81,11 @@ class BeanOverrideContextCustomizer implements ContextCustomizer { @@ -81,9 +81,11 @@ class BeanOverrideContextCustomizer implements ContextCustomizer {
@Override
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
if (context instanceof BeanDefinitionRegistry registry) {
registerInfrastructure(registry, this.detectedClasses);
if (!(context instanceof BeanDefinitionRegistry registry)) {
throw new IllegalStateException("Cannot process bean overrides with an ApplicationContext " +
"that doesn't implement BeanDefinitionRegistry: " + context.getClass());
}
registerInfrastructure(registry, this.detectedClasses);
}
@Override

7
spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessor.java

@ -111,9 +111,8 @@ class TestBeanOverrideProcessor implements BeanOverrideProcessor { @@ -111,9 +111,8 @@ class TestBeanOverrideProcessor implements BeanOverrideProcessor {
@Override
public TestBeanOverrideMetadata createMetadata(Annotation overrideAnnotation, Class<?> testClass, Field field) {
if (!(overrideAnnotation instanceof TestBean testBeanAnnotation)) {
throw new IllegalStateException(String.format("Invalid annotation passed to %s: expected @TestBean on field %s.%s",
TestBeanOverrideProcessor.class.getSimpleName(), field.getDeclaringClass().getName(),
field.getName()));
throw new IllegalStateException("Invalid annotation passed to %s: expected @TestBean on field %s.%s"
.formatted(getClass().getSimpleName(), field.getDeclaringClass().getName(), field.getName()));
}
Method overrideMethod;
String methodName = testBeanAnnotation.methodName();
@ -172,7 +171,7 @@ class TestBeanOverrideProcessor implements BeanOverrideProcessor { @@ -172,7 +171,7 @@ class TestBeanOverrideProcessor implements BeanOverrideProcessor {
return this.overrideMethod.invoke(null);
}
catch (IllegalAccessException | InvocationTargetException ex) {
throw new IllegalArgumentException("Could not invoke bean overriding method " + this.overrideMethod.getName() +
throw new IllegalStateException("Failed to invoke bean overriding method " + this.overrideMethod.getName() +
"; a static method with no formal parameters is expected", ex);
}
}

12
spring-test/src/test/java/org/springframework/test/context/bean/override/convention/FailingTestBeanByTypeIntegrationTests.java

@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
package org.springframework.test.context.bean.override.convention;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Bean;
@ -49,8 +51,8 @@ class FailingTestBeanByTypeIntegrationTests { @@ -49,8 +51,8 @@ class FailingTestBeanByTypeIntegrationTests {
cause(
instanceOf(IllegalStateException.class),
message("""
Unable to select a bean definition to override: 0 bean definitions \
found of type %s (as required by annotated field '%s.example')"""
Unable to select a bean definition to override: found 0 bean definitions \
of type %s (as required by annotated field '%s.example')"""
.formatted(ExampleService.class.getName(), testClass.getSimpleName())))));
}
@ -62,9 +64,9 @@ class FailingTestBeanByTypeIntegrationTests { @@ -62,9 +64,9 @@ class FailingTestBeanByTypeIntegrationTests {
cause(
instanceOf(IllegalStateException.class),
message("""
Unable to select a bean definition to override: 2 bean definitions \
found of type %s (as required by annotated field '%s.example')"""
.formatted(ExampleService.class.getName(), testClass.getSimpleName())))));
Unable to select a bean definition to override: found 2 bean definitions \
of type %s (as required by annotated field '%s.example'): %s"""
.formatted(ExampleService.class.getName(), testClass.getSimpleName(), List.of("bean1", "bean2"))))));
}

12
spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/FailingMockitoBeanByTypeIntegrationTests.java

@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
package org.springframework.test.context.bean.override.mockito;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Bean;
@ -49,8 +51,8 @@ class FailingMockitoBeanByTypeIntegrationTests { @@ -49,8 +51,8 @@ class FailingMockitoBeanByTypeIntegrationTests {
cause(
instanceOf(IllegalStateException.class),
message("""
Unable to select a bean definition to override: 0 bean definitions \
found of type %s (as required by annotated field '%s.example')"""
Unable to select a bean definition to override: found 0 bean definitions \
of type %s (as required by annotated field '%s.example')"""
.formatted(ExampleService.class.getName(), testClass.getSimpleName())))));
}
@ -62,9 +64,9 @@ class FailingMockitoBeanByTypeIntegrationTests { @@ -62,9 +64,9 @@ class FailingMockitoBeanByTypeIntegrationTests {
cause(
instanceOf(IllegalStateException.class),
message("""
Unable to select a bean definition to override: 2 bean definitions \
found of type %s (as required by annotated field '%s.example')"""
.formatted(ExampleService.class.getName(), testClass.getSimpleName())))));
Unable to select a bean definition to override: found 2 bean definitions \
of type %s (as required by annotated field '%s.example'): %s"""
.formatted(ExampleService.class.getName(), testClass.getSimpleName(), List.of("bean1", "bean2"))))));
}

10
spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/FailingMockitoSpyBeanByTypeIntegrationTests.java

@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
package org.springframework.test.context.bean.override.mockito;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Bean;
@ -48,7 +50,7 @@ class FailingMockitoSpyBeanByTypeIntegrationTests { @@ -48,7 +50,7 @@ class FailingMockitoSpyBeanByTypeIntegrationTests {
cause(
instanceOf(IllegalStateException.class),
message("""
Unable to select a bean to override by wrapping: 0 bean instances found of \
Unable to select a bean to override by wrapping: found 0 bean instances of \
type %s (as required by annotated field '%s.example')"""
.formatted(ExampleService.class.getName(), testClass.getSimpleName())))));
}
@ -61,9 +63,9 @@ class FailingMockitoSpyBeanByTypeIntegrationTests { @@ -61,9 +63,9 @@ class FailingMockitoSpyBeanByTypeIntegrationTests {
cause(
instanceOf(IllegalStateException.class),
message("""
Unable to select a bean to override by wrapping: 2 bean instances found of \
type %s (as required by annotated field '%s.example')"""
.formatted(ExampleService.class.getName(), testClass.getSimpleName())))));
Unable to select a bean to override by wrapping: found 2 bean instances of \
type %s (as required by annotated field '%s.example'): %s"""
.formatted(ExampleService.class.getName(), testClass.getSimpleName(), List.of("bean1", "bean2"))))));
}

2
spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/FailingMockitoSpyBeanIntegrationTests.java

@ -44,7 +44,7 @@ class FailingMockitoSpyBeanIntegrationTests { @@ -44,7 +44,7 @@ class FailingMockitoSpyBeanIntegrationTests {
finishedWithFailure(
cause(instanceOf(IllegalStateException.class),
message("""
Unable to select a bean to override by wrapping: 0 bean instances found \
Unable to select a bean to override by wrapping: found 0 bean instances \
of type %s (as required by annotated field '%s.notPresent')"""
.formatted(ExampleService.class.getName(), testClass.getSimpleName())))));
}

Loading…
Cancel
Save