diff --git a/spring-test/src/main/java/org/springframework/test/context/support/ContextLoaderUtils.java b/spring-test/src/main/java/org/springframework/test/context/support/ContextLoaderUtils.java index 96527a38674..f0296909257 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/ContextLoaderUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/ContextLoaderUtils.java @@ -243,7 +243,7 @@ abstract class ContextLoaderUtils { private static void resolveDefaultContextConfigurationAttributes( List results, Class testClass) { - results.add(0, new ContextConfigurationAttributes(testClass)); + results.add(new ContextConfigurationAttributes(testClass)); Class superclass = testClass.getSuperclass(); if (superclass != null && superclass != Object.class) { diff --git a/spring-test/src/test/java/org/springframework/test/context/config/ImplicitDefaultConfigClassesBaseTests.java b/spring-test/src/test/java/org/springframework/test/context/config/ImplicitDefaultConfigClassesBaseTests.java new file mode 100644 index 00000000000..3c7bd9d3abe --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/config/ImplicitDefaultConfigClassesBaseTests.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.config; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Base tests for detection of implicit configuration classes. + * + * @author Sam Brannen + * @since 7.0.2 + * @see DefaultConfigClassesBaseTests + */ +@ExtendWith(SpringExtension.class) +class ImplicitDefaultConfigClassesBaseTests { + + @Autowired + String greeting1; + + @Test + void greeting1() { + // This class must NOT be annotated with @SpringJUnitConfig or @ContextConfiguration. + assertThat(AnnotatedElementUtils.hasAnnotation(getClass(), ContextConfiguration.class)).isFalse(); + + assertThat(greeting1).isEqualTo("TEST 1"); + } + + @Configuration + static class DefaultConfig { + + @Bean + String greeting1() { + return "TEST 1"; + } + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/config/ImplicitDefaultConfigClassesInheritedTests.java b/spring-test/src/test/java/org/springframework/test/context/config/ImplicitDefaultConfigClassesInheritedTests.java new file mode 100644 index 00000000000..8960f876abd --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/config/ImplicitDefaultConfigClassesInheritedTests.java @@ -0,0 +1,65 @@ +/* + * Copyright 2002-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.config; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.test.context.ContextConfiguration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Inherited tests for detection of implicit configuration classes. + * + * @author Sam Brannen + * @since 7.0.2 + * @see DefaultConfigClassesInheritedTests + */ +class ImplicitDefaultConfigClassesInheritedTests extends ImplicitDefaultConfigClassesBaseTests { + + @Autowired + String greeting2; + + @Test + void greeting2() { + // This class must NOT be annotated with @SpringJUnitConfig or @ContextConfiguration. + assertThat(AnnotatedElementUtils.hasAnnotation(getClass(), ContextConfiguration.class)).isFalse(); + + assertThat(greeting2).isEqualTo("TEST 2"); + } + + @Test + void greetings(@Autowired List greetings) { + assertThat(greetings).containsExactly("TEST 1", "TEST 2"); + } + + @Configuration + static class DefaultConfig { + + @Bean + String greeting2() { + return "TEST 2"; + } + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/support/ContextLoaderUtilsConfigurationAttributesTests.java b/spring-test/src/test/java/org/springframework/test/context/support/ContextLoaderUtilsConfigurationAttributesTests.java index 810031a9d20..df6d47b5e99 100644 --- a/spring-test/src/test/java/org/springframework/test/context/support/ContextLoaderUtilsConfigurationAttributesTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/support/ContextLoaderUtilsConfigurationAttributesTests.java @@ -28,6 +28,7 @@ import org.springframework.test.context.ContextLoader; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.test.context.support.ContextLoaderUtils.resolveContextConfigurationAttributes; +import static org.springframework.test.context.support.ContextLoaderUtils.resolveDefaultContextConfigurationAttributes; /** * Tests for {@link ContextLoaderUtils} involving {@link ContextConfigurationAttributes}. @@ -164,8 +165,76 @@ class ContextLoaderUtilsConfigurationAttributesTests extends AbstractContextConf assertThat(attributesList).hasSize(1); } + @Test + void resolveDefaultContextConfigurationAttributesWithSuperclass() { + var attributesList = resolveDefaultContextConfigurationAttributes(Superclass.class); + assertThat(attributesList).hasSize(1); + + var configAttributes = attributesList.get(0); + assertThat(configAttributes.getDeclaringClass()).isEqualTo(Superclass.class); + assertDefaultAttributes(configAttributes); + } + + @Test + void resolveDefaultContextConfigurationAttributesWithSubclass() { + var attributesList = resolveDefaultContextConfigurationAttributes(Subclass.class); + assertThat(attributesList).hasSize(2); + + // For bottom-up semantics, Subclass must come before Superclass. + var configAttributes = attributesList.get(0); + assertThat(configAttributes.getDeclaringClass()).isEqualTo(Subclass.class); + assertDefaultAttributes(configAttributes); + + configAttributes = attributesList.get(1); + assertThat(configAttributes.getDeclaringClass()).isEqualTo(Superclass.class); + assertDefaultAttributes(configAttributes); + } + + @Test + void resolveDefaultContextConfigurationAttributesWithNestedClass() { + var attributesList = resolveDefaultContextConfigurationAttributes(Superclass.NestedTests.class); + assertThat(attributesList).hasSize(2); + + // For bottom-up semantics, Superclass.NestedTests must come before Superclass. + var configAttributes = attributesList.get(0); + assertThat(configAttributes.getDeclaringClass()).isEqualTo(Superclass.NestedTests.class); + assertDefaultAttributes(configAttributes); + + configAttributes = attributesList.get(1); + assertThat(configAttributes.getDeclaringClass()).isEqualTo(Superclass.class); + assertDefaultAttributes(configAttributes); + } + + @Test + void resolveDefaultContextConfigurationAttributesWithNestedClassesInSuperAndSubClasses() { + var attributesList = resolveDefaultContextConfigurationAttributes(Subclass.NestedTests.class); + assertThat(attributesList).hasSize(3); + + // For bottom-up semantics, Subclass.NestedTests must come before Subclass. + var configAttributes = attributesList.get(0); + assertThat(configAttributes.getDeclaringClass()).isEqualTo(Subclass.NestedTests.class); + assertDefaultAttributes(configAttributes); + + // For bottom-up semantics, Subclass must come before Superclass. + configAttributes = attributesList.get(1); + assertThat(configAttributes.getDeclaringClass()).isEqualTo(Subclass.class); + assertDefaultAttributes(configAttributes); + + configAttributes = attributesList.get(2); + assertThat(configAttributes.getDeclaringClass()).isEqualTo(Superclass.class); + assertDefaultAttributes(configAttributes); + } + + + private static void assertDefaultAttributes(ContextConfigurationAttributes configAttributes) { + assertThat(configAttributes.getClasses()).isEmpty(); + assertThat(configAttributes.getLocations()).isEmpty(); + assertThat(configAttributes.getInitializers()).isEmpty(); + assertThat(configAttributes.getContextLoaderClass()).isEqualTo(ContextLoader.class); + assertThat(configAttributes.isInheritInitializers()).isTrue(); + assertThat(configAttributes.isInheritLocations()).isTrue(); + } - // ------------------------------------------------------------------------- @ContextConfiguration(value = "x", locations = "y") private static class ConflictingLocations { @@ -175,4 +244,16 @@ class ContextLoaderUtilsConfigurationAttributesTests extends AbstractContextConf private static class LocationsAndClasses { } + static class Superclass { + // @Nested + class NestedTests { + } + } + + static class Subclass extends Superclass { + // @Nested + class NestedTests { + } + } + }