Skip to content

Commit

Permalink
Fix bug in BootstrapUtils.resolveDefaultTestContextBootstrapper()
Browse files Browse the repository at this point in the history
Use MetaAnnotationUtils.findMergedAnnotation() instead of the
MergedAnnotations API in order to provide proper support for
@NestedTestConfiguration.

See gh-19930
  • Loading branch information
sbrannen committed Oct 10, 2020
1 parent d9507a0 commit a71df12
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.springframework.test.context;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.LinkedHashSet;
import java.util.Set;
Expand All @@ -24,9 +25,6 @@
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.BeanUtils;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
import org.springframework.core.annotation.RepeatableContainers;
import org.springframework.lang.Nullable;
import org.springframework.test.util.MetaAnnotationUtils;
import org.springframework.test.util.MetaAnnotationUtils.AnnotationDescriptor;
Expand Down Expand Up @@ -60,6 +58,8 @@ abstract class BootstrapUtils {
private static final String WEB_APP_CONFIGURATION_ANNOTATION_CLASS_NAME =
"org.springframework.test.context.web.WebAppConfiguration";

private static final Class<? extends Annotation> webAppConfigurationClass = loadWebAppConfigurationClass();

private static final Log logger = LogFactory.getLog(BootstrapUtils.class);


Expand Down Expand Up @@ -180,14 +180,22 @@ private static Class<?> resolveExplicitTestContextBootstrapper(Class<?> testClas
}

private static Class<?> resolveDefaultTestContextBootstrapper(Class<?> testClass) throws Exception {
SearchStrategy searchStrategy = MetaAnnotationUtils.getSearchStrategy(testClass);
boolean webApp = MergedAnnotations.from(testClass, searchStrategy, RepeatableContainers.none())
.isPresent(WEB_APP_CONFIGURATION_ANNOTATION_CLASS_NAME);
ClassLoader classLoader = BootstrapUtils.class.getClassLoader();
if (webApp) {
return ClassUtils.forName(DEFAULT_WEB_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME, classLoader);
boolean webApp = (MetaAnnotationUtils.findMergedAnnotation(testClass, webAppConfigurationClass) != null);
String bootstrapperClassName = (webApp ? DEFAULT_WEB_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME :
DEFAULT_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME);
return ClassUtils.forName(bootstrapperClassName, BootstrapUtils.class.getClassLoader());
}

@SuppressWarnings("unchecked")
private static Class<? extends Annotation> loadWebAppConfigurationClass() {
try {
return (Class<? extends Annotation>) ClassUtils.forName(WEB_APP_CONFIGURATION_ANNOTATION_CLASS_NAME,
BootstrapUtils.class.getClassLoader());
}
catch (ClassNotFoundException | LinkageError ex) {
throw new IllegalStateException(
"Failed to load class for @" + WEB_APP_CONFIGURATION_ANNOTATION_CLASS_NAME, ex);
}
return ClassUtils.forName(DEFAULT_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME, classLoader);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,12 @@
import org.springframework.test.context.BootstrapUtilsTests.OuterClass.NestedWithInheritedBootstrapper.DoubleNestedWithOverriddenBootstrapper;
import org.springframework.test.context.BootstrapUtilsTests.OuterClass.NestedWithInheritedBootstrapper.DoubleNestedWithOverriddenBootstrapper.TripleNestedWithInheritedBootstrapper;
import org.springframework.test.context.BootstrapUtilsTests.OuterClass.NestedWithInheritedBootstrapper.DoubleNestedWithOverriddenBootstrapper.TripleNestedWithInheritedBootstrapperButLocalOverride;
import org.springframework.test.context.BootstrapUtilsTests.WebAppConfigClass.NestedWithInheritedWebConfig;
import org.springframework.test.context.BootstrapUtilsTests.WebAppConfigClass.NestedWithInheritedWebConfig.DoubleNestedWithImplicitlyInheritedWebConfig;
import org.springframework.test.context.BootstrapUtilsTests.WebAppConfigClass.NestedWithInheritedWebConfig.DoubleNestedWithOverriddenWebConfig;
import org.springframework.test.context.BootstrapUtilsTests.WebAppConfigClass.NestedWithInheritedWebConfig.DoubleNestedWithOverriddenWebConfig.TripleNestedWithInheritedOverriddenWebConfig;
import org.springframework.test.context.BootstrapUtilsTests.WebAppConfigClass.NestedWithInheritedWebConfig.DoubleNestedWithOverriddenWebConfig.TripleNestedWithInheritedOverriddenWebConfigAndTestInterface;
import org.springframework.test.context.support.DefaultTestContextBootstrapper;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.context.web.WebTestContextBootstrapper;

import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -77,11 +81,6 @@ void resolveTestContextBootstrapperForNonAnnotatedClass() {
assertBootstrapper(NonAnnotatedClass.class, DefaultTestContextBootstrapper.class);
}

@Test
void resolveTestContextBootstrapperForWebAppConfigurationAnnotatedClass() {
assertBootstrapper(WebAppConfigurationAnnotatedClass.class, WebTestContextBootstrapper.class);
}

@Test
void resolveTestContextBootstrapperWithDirectBootstrapWithAnnotation() {
assertBootstrapper(DirectBootstrapWithAnnotationClass.class, FooBootstrapper.class);
Expand Down Expand Up @@ -118,7 +117,14 @@ static Stream<Arguments> resolveTestContextBootstrapperInEnclosingClassHierarchy
args(DoubleNestedWithInheritedButOverriddenBootstrapper.class, EnigmaBootstrapper.class),//
args(DoubleNestedWithOverriddenBootstrapper.class, BarBootstrapper.class),//
args(TripleNestedWithInheritedBootstrapper.class, BarBootstrapper.class),//
args(TripleNestedWithInheritedBootstrapperButLocalOverride.class, EnigmaBootstrapper.class)//
args(TripleNestedWithInheritedBootstrapperButLocalOverride.class, EnigmaBootstrapper.class),//
// @WebAppConfiguration and default bootstrapper
args(WebAppConfigClass.class, WebTestContextBootstrapper.class),//
args(NestedWithInheritedWebConfig.class, WebTestContextBootstrapper.class),//
args(DoubleNestedWithImplicitlyInheritedWebConfig.class, WebTestContextBootstrapper.class),//
args(DoubleNestedWithOverriddenWebConfig.class, DefaultTestContextBootstrapper.class),//
args(TripleNestedWithInheritedOverriddenWebConfig.class, WebTestContextBootstrapper.class),//
args(TripleNestedWithInheritedOverriddenWebConfigAndTestInterface.class, DefaultTestContextBootstrapper.class)//
);
}

Expand Down Expand Up @@ -189,8 +195,36 @@ static class DuplicateMetaAnnotatedBootstrapWithAnnotationClass {}
@BootstrapWith(EnigmaBootstrapper.class)
static class LocalDeclarationAndMetaAnnotatedBootstrapWithAnnotationClass {}

@WebAppConfiguration
static class WebAppConfigurationAnnotatedClass {}
@org.springframework.test.context.web.WebAppConfiguration
static class WebAppConfigClass {

@NestedTestConfiguration(INHERIT)
class NestedWithInheritedWebConfig {

class DoubleNestedWithImplicitlyInheritedWebConfig {
}

@NestedTestConfiguration(OVERRIDE)
class DoubleNestedWithOverriddenWebConfig {

@NestedTestConfiguration(INHERIT)
@org.springframework.test.context.web.WebAppConfiguration
class TripleNestedWithInheritedOverriddenWebConfig {
}

@NestedTestConfiguration(INHERIT)
class TripleNestedWithInheritedOverriddenWebConfigAndTestInterface {
}
}
}

// Intentionally not annotated with @WebAppConfiguration to ensure that
// TripleNestedWithInheritedOverriddenWebConfigAndTestInterface is not
// considered to be annotated with @WebAppConfiguration even though the
// enclosing class for TestInterface is annotated with @WebAppConfiguration.
interface TestInterface {
}
}

@BootWithFoo
static class OuterClass {
Expand Down

0 comments on commit a71df12

Please sign in to comment.