Browse Source

Allow directly present @BootstrapWith to override meta annotations

Prior to this commit, if a test class was meta-annotated with multiple
@BootstrapWith declarations that registered different
TestContextBootstrapper implementations, such a configuration would
result in an IllegalStateException, and there was no way to override
this behavior.

This commit addresses this shortcoming by relaxing the explicit
TestContextBootstrapper resolution in BootstrapUtils so that a directly
present @BootstrapWith annotation will now override declarations of
@BootstrapWith that are meta-present. In other words, if @BootstrapWith
is used as a meta-annotation, it can be overridden directly on the test
class via an explicit, local declaration of @BootstrapWith.

Issue: SPR-17006
pull/1929/head
Sam Brannen 8 years ago
parent
commit
d20d95b7ac
  1. 14
      spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java
  2. 17
      spring-test/src/test/java/org/springframework/test/context/BootstrapUtilsTests.java

14
spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java

@ -26,7 +26,6 @@ import org.springframework.beans.BeanUtils;
import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
/** /**
@ -154,10 +153,19 @@ abstract class BootstrapUtils {
if (annotations.isEmpty()) { if (annotations.isEmpty()) {
return null; return null;
} }
Assert.state(annotations.size() <= 1, () -> String.format( if (annotations.size() == 1) {
return annotations.iterator().next().value();
}
// Allow directly-present annotation to override annotations that are meta-present.
BootstrapWith bootstrapWith = testClass.getDeclaredAnnotation(BootstrapWith.class);
if (bootstrapWith != null) {
return bootstrapWith.value();
}
throw new IllegalStateException(String.format(
"Configuration error: found multiple declarations of @BootstrapWith for test class [%s]: %s", "Configuration error: found multiple declarations of @BootstrapWith for test class [%s]: %s",
testClass.getName(), annotations)); testClass.getName(), annotations));
return annotations.iterator().next().value();
} }
private static Class<?> resolveDefaultTestContextBootstrapper(Class<?> testClass) throws Exception { private static Class<?> resolveDefaultTestContextBootstrapper(Class<?> testClass) throws Exception {

17
spring-test/src/test/java/org/springframework/test/context/BootstrapUtilsTests.java

@ -99,6 +99,14 @@ public class BootstrapUtilsTests {
assertBootstrapper(DuplicateMetaAnnotatedBootstrapWithAnnotationClass.class, FooBootstrapper.class); assertBootstrapper(DuplicateMetaAnnotatedBootstrapWithAnnotationClass.class, FooBootstrapper.class);
} }
/**
* @since 5.1
*/
@Test
public void resolveTestContextBootstrapperWithLocalDeclarationThatOverridesMetaBootstrapWithAnnotations() {
assertBootstrapper(LocalDeclarationAndMetaAnnotatedBootstrapWithAnnotationClass.class, EnigmaBootstrapper.class);
}
private void assertBootstrapper(Class<?> testClass, Class<?> expectedBootstrapper) { private void assertBootstrapper(Class<?> testClass, Class<?> expectedBootstrapper) {
BootstrapContext bootstrapContext = BootstrapTestUtils.buildBootstrapContext(testClass, delegate); BootstrapContext bootstrapContext = BootstrapTestUtils.buildBootstrapContext(testClass, delegate);
TestContextBootstrapper bootstrapper = resolveTestContextBootstrapper(bootstrapContext); TestContextBootstrapper bootstrapper = resolveTestContextBootstrapper(bootstrapContext);
@ -112,6 +120,8 @@ public class BootstrapUtilsTests {
static class BarBootstrapper extends DefaultTestContextBootstrapper {} static class BarBootstrapper extends DefaultTestContextBootstrapper {}
static class EnigmaBootstrapper extends DefaultTestContextBootstrapper {}
@BootstrapWith(FooBootstrapper.class) @BootstrapWith(FooBootstrapper.class)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@interface BootWithFoo {} @interface BootWithFoo {}
@ -146,7 +156,12 @@ public class BootstrapUtilsTests {
@BootWithFoo @BootWithFoo
@BootWithFooAgain @BootWithFooAgain
static class DuplicateMetaAnnotatedBootstrapWithAnnotationClass {} static class DuplicateMetaAnnotatedBootstrapWithAnnotationClass {}
@BootWithFoo
@BootWithBar
@BootstrapWith(EnigmaBootstrapper.class)
static class LocalDeclarationAndMetaAnnotatedBootstrapWithAnnotationClass {}
@WebAppConfiguration @WebAppConfiguration
static class WebAppConfigurationAnnotatedClass {} static class WebAppConfigurationAnnotatedClass {}

Loading…
Cancel
Save