From 8004a821f2dcd7a5038578af39344375af38cb01 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 2 Nov 2020 20:40:27 +0000 Subject: [PATCH] Fix nested test config discovery for sliced tests Fixes gh-23984 --- ...xcludeFiltersContextCustomizerFactory.java | 14 ++-- .../properties/AnnotationsPropertySource.java | 10 ++- ...eFiltersContextCustomizerFactoryTests.java | 19 ++++- .../AnnotationsPropertySourceTests.java | 29 ++++++++ .../WebMvcTestNestedIntegrationTests.java | 70 +++++++++++++++++++ 5 files changed, 133 insertions(+), 9 deletions(-) create mode 100644 spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/WebMvcTestNestedIntegrationTests.java diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/filter/TypeExcludeFiltersContextCustomizerFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/filter/TypeExcludeFiltersContextCustomizerFactory.java index b93afdcfc47e..e0c748fd49d1 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/filter/TypeExcludeFiltersContextCustomizerFactory.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/filter/TypeExcludeFiltersContextCustomizerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -21,12 +21,11 @@ import java.util.List; import org.springframework.boot.context.TypeExcludeFilter; -import org.springframework.core.annotation.MergedAnnotation; -import org.springframework.core.annotation.MergedAnnotations; -import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.test.context.ContextConfigurationAttributes; import org.springframework.test.context.ContextCustomizer; import org.springframework.test.context.ContextCustomizerFactory; +import org.springframework.test.context.TestContextAnnotationUtils; +import org.springframework.test.context.TestContextAnnotationUtils.AnnotationDescriptor; import org.springframework.util.ObjectUtils; /** @@ -43,12 +42,13 @@ class TypeExcludeFiltersContextCustomizerFactory implements ContextCustomizerFac @Override public ContextCustomizer createContextCustomizer(Class testClass, List configurationAttributes) { - Class[] filterClasses = MergedAnnotations.from(testClass, SearchStrategy.INHERITED_ANNOTATIONS) - .get(TypeExcludeFilters.class).getValue(MergedAnnotation.VALUE, Class[].class).orElse(NO_FILTERS); + AnnotationDescriptor descriptor = TestContextAnnotationUtils + .findAnnotationDescriptor(testClass, TypeExcludeFilters.class); + Class[] filterClasses = (descriptor != null) ? descriptor.getAnnotation().value() : NO_FILTERS; if (ObjectUtils.isEmpty(filterClasses)) { return null; } - return createContextCustomizer(testClass, filterClasses); + return createContextCustomizer(descriptor.getRootDeclaringClass(), filterClasses); } @SuppressWarnings("unchecked") diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/properties/AnnotationsPropertySource.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/properties/AnnotationsPropertySource.java index a3eadd734226..d3432e3f3fc6 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/properties/AnnotationsPropertySource.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/properties/AnnotationsPropertySource.java @@ -30,6 +30,7 @@ import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.test.context.TestContextAnnotationUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -58,6 +59,11 @@ public AnnotationsPropertySource(String name, Class source) { private Map getProperties(Class source) { Map properties = new LinkedHashMap<>(); + getProperties(source, properties); + return properties; + } + + private void getProperties(Class source, Map properties) { MergedAnnotations.from(source, SearchStrategy.SUPERCLASS).stream() .filter(MergedAnnotationPredicates.unique(MergedAnnotation::getType)).forEach((annotation) -> { Class type = annotation.getType(); @@ -70,7 +76,9 @@ private Map getProperties(Class source) { collectProperties(prefix, defaultSkip, annotation, attribute, properties); } }); - return properties; + if (TestContextAnnotationUtils.searchEnclosingClass(source)) { + getProperties(source.getEnclosingClass(), properties); + } } private void collectProperties(String prefix, SkipPropertyMapping skip, MergedAnnotation annotation, diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/filter/TypeExcludeFiltersContextCustomizerFactoryTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/filter/TypeExcludeFiltersContextCustomizerFactoryTests.java index 852a084b2829..a2424ef339e1 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/filter/TypeExcludeFiltersContextCustomizerFactoryTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/filter/TypeExcludeFiltersContextCustomizerFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -19,6 +19,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.context.TypeExcludeFilter; +import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizerFactoryTests.EnclosingClass.WithEnclosingClassExcludeFilters; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.type.classreading.MetadataReader; @@ -55,6 +56,13 @@ void getContextCustomizerWhenHasAnnotationShouldReturnCustomizer() { assertThat(customizer).isNotNull(); } + @Test + void getContextCustomizerWhenEnclosingClassHasAnnotationShouldReturnCustomizer() { + ContextCustomizer customizer = this.factory.createContextCustomizer(WithEnclosingClassExcludeFilters.class, + null); + assertThat(customizer).isNotNull(); + } + @Test void hashCodeAndEquals() { ContextCustomizer customizer1 = this.factory.createContextCustomizer(WithExcludeFilters.class, null); @@ -88,6 +96,15 @@ static class WithExcludeFilters { } + @TypeExcludeFilters({ SimpleExclude.class, TestClassAwareExclude.class }) + static class EnclosingClass { + + class WithEnclosingClassExcludeFilters { + + } + + } + @TypeExcludeFilters({ TestClassAwareExclude.class, SimpleExclude.class }) static class WithSameExcludeFilters { diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/properties/AnnotationsPropertySourceTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/properties/AnnotationsPropertySourceTests.java index bbdf7a885529..d52d5730f1e3 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/properties/AnnotationsPropertySourceTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/properties/AnnotationsPropertySourceTests.java @@ -19,10 +19,12 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.properties.AnnotationsPropertySourceTests.DeeplyNestedAnnotations.Level1; import org.springframework.boot.test.autoconfigure.properties.AnnotationsPropertySourceTests.DeeplyNestedAnnotations.Level2; +import org.springframework.boot.test.autoconfigure.properties.AnnotationsPropertySourceTests.EnclosingClass.PropertyMappedAnnotationOnEnclosingClass; import org.springframework.boot.test.autoconfigure.properties.AnnotationsPropertySourceTests.NestedAnnotations.Entry; import org.springframework.core.annotation.AliasFor; @@ -155,6 +157,14 @@ void typeLevelAnnotationOnSuperClass() { assertThat(source.getProperty("value")).isEqualTo("abc"); } + @Test + void typeLevelAnnotationOnEnclosingClass() { + AnnotationsPropertySource source = new AnnotationsPropertySource( + PropertyMappedAnnotationOnEnclosingClass.class); + assertThat(source.getPropertyNames()).containsExactly("value"); + assertThat(source.getProperty("value")).isEqualTo("abc"); + } + @Test void aliasedPropertyMappedAttributeOnSuperClass() { AnnotationsPropertySource source = new AnnotationsPropertySource( @@ -386,6 +396,15 @@ static class PropertyMappedAnnotationOnSuperClass extends TypeLevel { } + @TypeLevelAnnotation("abc") + static class EnclosingClass { + + class PropertyMappedAnnotationOnEnclosingClass { + + } + + } + static class AliasedPropertyMappedAnnotationOnSuperClass extends PropertyMappedAttributeWithAnAlias { } @@ -466,4 +485,14 @@ static class PropertyMappedWithDeeplyNestedAnnotations { } + @TypeLevelAnnotation("outer") + static class OuterWithTypeLevel { + + @Nested + static class NestedClass { + + } + + } + } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/WebMvcTestNestedIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/WebMvcTestNestedIntegrationTests.java new file mode 100644 index 000000000000..da07a8b3004f --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/WebMvcTestNestedIntegrationTests.java @@ -0,0 +1,70 @@ +/* + * Copyright 2012-2020 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.boot.test.autoconfigure.web.servlet.mockmvc; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Tests for {@link WebMvcTest @WebMvcTest} using {@link Nested}. + * + * @author Andy Wilkinson + */ +@WebMvcTest(controllers = ExampleController2.class) +@WithMockUser +class WebMvcTestNestedIntegrationTests { + + @Autowired + private MockMvc mvc; + + @Test + void shouldNotFindController1() throws Exception { + this.mvc.perform(get("/one")).andExpect(status().isNotFound()); + } + + @Test + void shouldFindController2() throws Exception { + this.mvc.perform(get("/two")).andExpect(content().string("hellotwo")).andExpect(status().isOk()); + } + + @Nested + @WithMockUser + class NestedTests { + + @Test + void shouldNotFindController1() throws Exception { + WebMvcTestNestedIntegrationTests.this.mvc.perform(get("/one")).andExpect(status().isNotFound()); + } + + @Test + void shouldFindController2() throws Exception { + WebMvcTestNestedIntegrationTests.this.mvc.perform(get("/two")).andExpect(content().string("hellotwo")) + .andExpect(status().isOk()); + } + + } + +}