From 1f017c4acb3a4b4acdf088bda966e8593349ba5b Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 1 Apr 2014 18:07:52 +0200 Subject: [PATCH] Support classes AND locations in @ContextConfiguration Prior to this commit, the Spring TestContext Framework did not support the declaration of both 'locations' and 'classes' within @ContextConfiguration at the same time. This commit addresses this in the following manner: - ContextConfigurationAttributes no longer throws an IllegalArgumentException if both 'locations' and 'classes' are supplied to its constructor. - Concrete SmartContextLoader implementations now validate the supplied MergedContextConfiguration before attempting to load the ApplicationContext. See validateMergedContextConfiguration(). - Introduced tests for hybrid context loaders like the one used in Spring Boot. See HybridContextLoaderTests. - Updated the Testing chapter of the reference manual so that it no longer states that locations and classes cannot be used simultaneously, mentioning Spring Boot as well. - The Javadoc for @ContextConfiguration has been updated accordingly. - Added hasLocations(), hasClasses(), and hasResources() convenience methods to MergedContextConfiguration. Issue: SPR-11634 --- spring-test/.springBeans | 5 +- .../test/context/ContextConfiguration.java | 24 +++--- .../ContextConfigurationAttributes.java | 31 ++++--- .../context/MergedContextConfiguration.java | 40 ++++++++++ .../support/AbstractGenericContextLoader.java | 20 ++++- .../AnnotationConfigContextLoader.java | 25 +++++- .../AnnotationConfigContextLoaderUtils.java | 4 +- .../GenericPropertiesContextLoader.java | 24 +++++- .../support/GenericXmlContextLoader.java | 24 +++++- .../web/AbstractGenericWebContextLoader.java | 23 +++++- .../web/AnnotationConfigWebContextLoader.java | 27 +++++-- .../web/GenericXmlWebContextLoader.java | 22 ++++- ...aderUtilsConfigurationAttributesTests.java | 17 ++++ .../junit4/hybrid/HybridContextLoader.java | 77 ++++++++++++++++++ .../HybridContextLoaderTests-context.xml | 9 +++ .../hybrid/HybridContextLoaderTests.java | 80 +++++++++++++++++++ .../AnnotationConfigContextLoaderTests.java | 31 ++++++- .../GenericPropertiesContextLoaderTests.java | 51 ++++++++++++ .../support/GenericXmlContextLoaderTests.java | 52 ++++++++++++ ...AnnotationConfigWebContextLoaderTests.java | 52 ++++++++++++ .../web/GenericXmlWebContextLoaderTests.java | 50 ++++++++++++ src/asciidoc/index.adoc | 23 ++++-- 22 files changed, 650 insertions(+), 61 deletions(-) create mode 100644 spring-test/src/test/java/org/springframework/test/context/junit4/hybrid/HybridContextLoader.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/junit4/hybrid/HybridContextLoaderTests-context.xml create mode 100644 spring-test/src/test/java/org/springframework/test/context/junit4/hybrid/HybridContextLoaderTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/support/GenericPropertiesContextLoaderTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/support/GenericXmlContextLoaderTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/web/AnnotationConfigWebContextLoaderTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/web/GenericXmlWebContextLoaderTests.java diff --git a/spring-test/.springBeans b/spring-test/.springBeans index aa53e0e658..dc7397a838 100644 --- a/spring-test/.springBeans +++ b/spring-test/.springBeans @@ -1,7 +1,7 @@ 1 - + @@ -11,7 +11,10 @@ src/test/java/org/springframework/test/context/junit4/aci/xml/MultipleInitializersXmlConfigTests-context.xml src/test/resources/org/springframework/test/context/web/RequestAndSessionScopedBeansWacTests-context.xml src/test/resources/org/springframework/test/context/hierarchies/web/DispatcherWacRootWacEarTests-context.xml + src/test/java/org/springframework/test/context/junit4/hybrid/hybrid-config.xml + + diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java index 6018f06e57..1b3ea9f274 100644 --- a/spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java +++ b/spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -36,11 +36,14 @@ import org.springframework.context.ConfigurableApplicationContext; * *

Prior to Spring 3.1, only path-based resource locations were supported. * As of Spring 3.1, {@linkplain #loader context loaders} may choose to support - * either path-based or class-based resources (but not both). Consequently + * either path-based or class-based resources. As of Spring + * 4.0.4, {@linkplain #loader context loaders} may choose to support path-based + * and class-based resources simultaneously. Consequently * {@code @ContextConfiguration} can be used to declare either path-based - * resource locations (via the {@link #locations} or {@link #value} - * attribute) or annotated classes (via the {@link #classes} - * attribute). + * resource locations (via the {@link #locations} or {@link #value} attribute) + * or annotated classes (via the {@link #classes} attribute). Note, + * however, that most implementations of {@link SmartContextLoader} only support + * a single resource type. * *

Annotated Classes

* @@ -86,9 +89,8 @@ public @interface ContextConfiguration { /** * Alias for {@link #locations}. * - *

This attribute may not be used in conjunction - * with {@link #locations} or {@link #classes}, but it may be used - * instead of {@link #locations}. + *

This attribute may not be used in conjunction with + * {@link #locations}, but it may be used instead of {@link #locations}. * @since 3.0 * @see #inheritLocations */ @@ -117,8 +119,7 @@ public @interface ContextConfiguration { * loaders. * *

This attribute may not be used in conjunction with - * {@link #value} or {@link #classes}, but it may be used instead of - * {@link #value}. + * {@link #value}, but it may be used instead of {@link #value}. * @since 2.5 * @see #inheritLocations */ @@ -135,9 +136,6 @@ public @interface ContextConfiguration { * annotated classes are specified. See the documentation for * {@link #loader} for further details regarding default loaders. * - *

This attribute may not be used in conjunction with - * {@link #locations} or {@link #value}. - * * @since 3.1 * @see org.springframework.context.annotation.Configuration * @see org.springframework.test.context.support.AnnotationConfigContextLoader diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java b/spring-test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java index fcf1ab05b8..16569e5aea 100644 --- a/spring-test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java +++ b/spring-test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -29,9 +29,8 @@ import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** - * {@code ContextConfigurationAttributes} encapsulates the context - * configuration attributes declared on a test class via - * {@link ContextConfiguration @ContextConfiguration}. + * {@code ContextConfigurationAttributes} encapsulates the context configuration + * attributes declared via {@link ContextConfiguration @ContextConfiguration}. * * @author Sam Brannen * @since 3.1 @@ -111,8 +110,9 @@ public class ContextConfigurationAttributes { /** * Construct a new {@link ContextConfigurationAttributes} instance for the - * supplied {@link ContextConfiguration @ContextConfiguration} annotation and - * the {@linkplain Class test class} that declared it. + * supplied {@link AnnotationAttributes} (parsed from a + * {@link ContextConfiguration @ContextConfiguration} annotation) and + * the {@linkplain Class test class} that declared them. * @param declaringClass the test class that declared {@code @ContextConfiguration} * @param annAttrs the annotation attributes from which to retrieve the attributes */ @@ -140,7 +140,7 @@ public class ContextConfigurationAttributes { * @param inheritLocations the {@code inheritLocations} flag declared via {@code @ContextConfiguration} * @param contextLoaderClass the {@code ContextLoader} class declared via {@code @ContextConfiguration} * @throws IllegalArgumentException if the {@code declaringClass} or {@code contextLoaderClass} is - * {@code null}, or if the {@code locations} and {@code classes} are both non-empty + * {@code null} * @deprecated as of Spring 3.2, use * {@link #ContextConfigurationAttributes(Class, String[], Class[], boolean, Class[], boolean, String, Class)} * instead @@ -165,7 +165,7 @@ public class ContextConfigurationAttributes { * @param inheritInitializers the {@code inheritInitializers} flag declared via {@code @ContextConfiguration} * @param contextLoaderClass the {@code ContextLoader} class declared via {@code @ContextConfiguration} * @throws IllegalArgumentException if the {@code declaringClass} or {@code contextLoaderClass} is - * {@code null}, or if the {@code locations} and {@code classes} are both non-empty + * {@code null} */ public ContextConfigurationAttributes(Class declaringClass, String[] locations, Class[] classes, boolean inheritLocations, @@ -190,7 +190,7 @@ public class ContextConfigurationAttributes { * @param name the name of level in the context hierarchy, or {@code null} if not applicable * @param contextLoaderClass the {@code ContextLoader} class declared via {@code @ContextConfiguration} * @throws IllegalArgumentException if the {@code declaringClass} or {@code contextLoaderClass} is - * {@code null}, or if the {@code locations} and {@code classes} are both non-empty + * {@code null} */ public ContextConfigurationAttributes(Class declaringClass, String[] locations, Class[] classes, boolean inheritLocations, @@ -200,14 +200,13 @@ public class ContextConfigurationAttributes { Assert.notNull(declaringClass, "declaringClass must not be null"); Assert.notNull(contextLoaderClass, "contextLoaderClass must not be null"); - if (!ObjectUtils.isEmpty(locations) && !ObjectUtils.isEmpty(classes)) { - String msg = String.format( + if (!ObjectUtils.isEmpty(locations) && !ObjectUtils.isEmpty(classes) && logger.isDebugEnabled()) { + logger.debug(String.format( "Test class [%s] has been configured with @ContextConfiguration's 'locations' (or 'value') %s " - + "and 'classes' %s attributes. Only one declaration of resources " - + "is permitted per @ContextConfiguration annotation.", declaringClass.getName(), - ObjectUtils.nullSafeToString(locations), ObjectUtils.nullSafeToString(classes)); - logger.error(msg); - throw new IllegalArgumentException(msg); + + "and 'classes' %s attributes. Most SmartContextLoader implementations support " + + "only one declaration of resources per @ContextConfiguration annotation.", + declaringClass.getName(), ObjectUtils.nullSafeToString(locations), + ObjectUtils.nullSafeToString(classes))); } this.declaringClass = declaringClass; diff --git a/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java index befe85c7cb..5dc0f20cff 100644 --- a/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java +++ b/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java @@ -226,6 +226,46 @@ public class MergedContextConfiguration implements Serializable { return classes; } + /** + * Determine if this {@code MergedContextConfiguration} instance has + * path-based resource locations. + * + * @return {@code true} if the {@link #getLocations() locations} array is not empty + * @since 4.0.4 + * @see #hasResources() + * @see #hasClasses() + */ + public boolean hasLocations() { + return !ObjectUtils.isEmpty(getLocations()); + } + + /** + * Determine if this {@code MergedContextConfiguration} instance has + * class-based resources. + * + * @return {@code true} if the {@link #getClasses() classes} array is not empty + * @since 4.0.4 + * @see #hasResources() + * @see #hasLocations() + */ + public boolean hasClasses() { + return !ObjectUtils.isEmpty(getClasses()); + } + + /** + * Determine if this {@code MergedContextConfiguration} instance has + * either path-based resource locations or class-based resources. + * + * @return {@code true} if either the {@link #getLocations() locations} + * or the {@link #getClasses() classes} array is not empty + * @since 4.0.4 + * @see #hasLocations() + * @see #hasClasses() + */ + public boolean hasResources() { + return hasLocations() || hasClasses(); + } + /** * Get the merged {@code ApplicationContextInitializer} classes for the * {@linkplain #getTestClass() test class}. diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java index dc8bb8d68a..41e42fcf4e 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -68,6 +68,8 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader *

Implementation details: * *