Skip to content

Commit d20d95b

Browse files
committed
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
1 parent 4d7b265 commit d20d95b

File tree

2 files changed

+27
-4
lines changed

2 files changed

+27
-4
lines changed

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import org.springframework.core.annotation.AnnotatedElementUtils;
2727
import org.springframework.core.annotation.AnnotationAttributes;
2828
import org.springframework.lang.Nullable;
29-
import org.springframework.util.Assert;
3029
import org.springframework.util.ClassUtils;
3130

3231
/**
@@ -154,10 +153,19 @@ private static Class<?> resolveExplicitTestContextBootstrapper(Class<?> testClas
154153
if (annotations.isEmpty()) {
155154
return null;
156155
}
157-
Assert.state(annotations.size() <= 1, () -> String.format(
156+
if (annotations.size() == 1) {
157+
return annotations.iterator().next().value();
158+
}
159+
160+
// Allow directly-present annotation to override annotations that are meta-present.
161+
BootstrapWith bootstrapWith = testClass.getDeclaredAnnotation(BootstrapWith.class);
162+
if (bootstrapWith != null) {
163+
return bootstrapWith.value();
164+
}
165+
166+
throw new IllegalStateException(String.format(
158167
"Configuration error: found multiple declarations of @BootstrapWith for test class [%s]: %s",
159168
testClass.getName(), annotations));
160-
return annotations.iterator().next().value();
161169
}
162170

163171
private static Class<?> resolveDefaultTestContextBootstrapper(Class<?> testClass) throws Exception {

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,14 @@ public void resolveTestContextBootstrapperWithDuplicatingMetaBootstrapWithAnnota
9999
assertBootstrapper(DuplicateMetaAnnotatedBootstrapWithAnnotationClass.class, FooBootstrapper.class);
100100
}
101101

102+
/**
103+
* @since 5.1
104+
*/
105+
@Test
106+
public void resolveTestContextBootstrapperWithLocalDeclarationThatOverridesMetaBootstrapWithAnnotations() {
107+
assertBootstrapper(LocalDeclarationAndMetaAnnotatedBootstrapWithAnnotationClass.class, EnigmaBootstrapper.class);
108+
}
109+
102110
private void assertBootstrapper(Class<?> testClass, Class<?> expectedBootstrapper) {
103111
BootstrapContext bootstrapContext = BootstrapTestUtils.buildBootstrapContext(testClass, delegate);
104112
TestContextBootstrapper bootstrapper = resolveTestContextBootstrapper(bootstrapContext);
@@ -112,6 +120,8 @@ static class FooBootstrapper extends DefaultTestContextBootstrapper {}
112120

113121
static class BarBootstrapper extends DefaultTestContextBootstrapper {}
114122

123+
static class EnigmaBootstrapper extends DefaultTestContextBootstrapper {}
124+
115125
@BootstrapWith(FooBootstrapper.class)
116126
@Retention(RetentionPolicy.RUNTIME)
117127
@interface BootWithFoo {}
@@ -146,7 +156,12 @@ static class MetaAnnotatedBootstrapWithAnnotationClass {}
146156
@BootWithFoo
147157
@BootWithFooAgain
148158
static class DuplicateMetaAnnotatedBootstrapWithAnnotationClass {}
149-
159+
160+
@BootWithFoo
161+
@BootWithBar
162+
@BootstrapWith(EnigmaBootstrapper.class)
163+
static class LocalDeclarationAndMetaAnnotatedBootstrapWithAnnotationClass {}
164+
150165
@WebAppConfiguration
151166
static class WebAppConfigurationAnnotatedClass {}
152167

0 commit comments

Comments
 (0)