From 63723fadb6412ea502d09c4f22a41d88983432aa Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Mon, 2 Mar 2026 16:26:25 +0100 Subject: [PATCH] =?UTF-8?q?Introduce=20tests=20for=20additional=20@?= =?UTF-8?q?=E2=81=A0BootstrapWith=20use=20cases?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See gh-35938 --- .../test/context/BootstrapUtilsTests.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/spring-test/src/test/java/org/springframework/test/context/BootstrapUtilsTests.java b/spring-test/src/test/java/org/springframework/test/context/BootstrapUtilsTests.java index a63289371c0..f705ddad545 100644 --- a/spring-test/src/test/java/org/springframework/test/context/BootstrapUtilsTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/BootstrapUtilsTests.java @@ -25,6 +25,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.core.annotation.AliasFor; import org.springframework.test.context.BootstrapUtilsTests.OuterClass.NestedWithInheritedBootstrapper; import org.springframework.test.context.BootstrapUtilsTests.OuterClass.NestedWithInheritedBootstrapper.DoubleNestedWithInheritedButOverriddenBootstrapper; import org.springframework.test.context.BootstrapUtilsTests.OuterClass.NestedWithInheritedBootstrapper.DoubleNestedWithOverriddenBootstrapper; @@ -140,6 +141,37 @@ class BootstrapUtilsTests { assertBootstrapper(LocalDeclarationAndMetaAnnotatedBootstrapWithAnnotationClass.class, EnigmaBootstrapper.class); } + /** + * @since 7.0.6 + */ + @Test // gh-35938 + void resolveTestContextBootstrapperWithMetaBootstrapWithAnnotationThatOverridesMetaMetaBootstrapWithAnnotation() { + BootstrapContext bootstrapContext = BootstrapTestUtils.buildBootstrapContext( + MetaAndMetaMetaBootstrapWithAnnotationsClass.class, delegate); + assertThatIllegalStateException() + .isThrownBy(() -> resolveTestContextBootstrapper(bootstrapContext)) + .withMessageContaining("Configuration error: found multiple declarations of @BootstrapWith") + .withMessageContaining(FooBootstrapper.class.getSimpleName()) + .withMessageContaining(BarBootstrapper.class.getSimpleName()); + } + + /** + * @since 7.0.6 + */ + @Test // gh-35938 + void resolveTestContextBootstrapperWithComposedBootstrapWithAnnotationThatOverridesMetaMetaBootstrapWithAnnotation() { + assertBootstrapper(ConfigurableAndMetaMetaBootstrapWithAnnotationsClass.class, BarBootstrapper.class); + } + + /** + * @since 7.0.6 + */ + @Test // gh-35938 + void resolveTestContextBootstrapperWithCustomizedComposedBootstrapWithAnnotationThatOverridesMetaMetaBootstrapWithAnnotation() { + assertBootstrapper(CustomizedConfigurableAndMetaMetaBootstrapWithAnnotationsClass.class, EnigmaBootstrapper.class); + } + + private void assertBootstrapper(Class testClass, Class expectedBootstrapper) { BootstrapContext bootstrapContext = BootstrapTestUtils.buildBootstrapContext(testClass, delegate); TestContextBootstrapper bootstrapper = resolveTestContextBootstrapper(bootstrapContext); @@ -167,6 +199,28 @@ class BootstrapUtilsTests { @Retention(RetentionPolicy.RUNTIME) @interface BootWithBar {} + /** + * This annotation emulates the expectation of certain Spring Boot users who wish + * to be able to override the {@code @BootstrapWith} meta-annotation + * declaration on {@code @SpringBootTest} while retaining the other configuration of + * {@code @SpringBootTest}. + *

In this annotation, {@code @BootWithFoo} plays the role of {@code @SpringBootTest}, + * which the user wishes to override via a local declaration of + * {@code @BootstrapWith(BarBootstrapper.class)}. + */ + @BootWithFoo + @BootstrapWith(BarBootstrapper.class) + @Retention(RetentionPolicy.RUNTIME) + @interface BootWithBarInsteadOfFoo {} + + @BootWithFoo + @Retention(RetentionPolicy.RUNTIME) + @interface BootWithConfigurableBootstrapperInsteadOfFoo { + + @AliasFor(annotation = BootstrapWith.class) + Class value() default BarBootstrapper.class; + } + // Invalid @BootstrapWith static class EmptyBootstrapWithAnnotationClass {} @@ -195,6 +249,15 @@ class BootstrapUtilsTests { @BootstrapWith(EnigmaBootstrapper.class) static class LocalDeclarationAndMetaAnnotatedBootstrapWithAnnotationClass {} + @BootWithBarInsteadOfFoo + static class MetaAndMetaMetaBootstrapWithAnnotationsClass {} + + @BootWithConfigurableBootstrapperInsteadOfFoo + static class ConfigurableAndMetaMetaBootstrapWithAnnotationsClass {} + + @BootWithConfigurableBootstrapperInsteadOfFoo(EnigmaBootstrapper.class) + static class CustomizedConfigurableAndMetaMetaBootstrapWithAnnotationsClass {} + @org.springframework.test.context.web.WebAppConfiguration static class WebAppConfigClass {