Support ApplicationContextInitializers in the TCF

Starting with Spring 3.1 applications can specify
contextInitializerClasses via context-param and init-param in web.xml;
however, there is currently no way to have such initializers invoked in
integration testing scenarios without writing a custom
SmartContextLoader. For comprehensive integration testing it should
therefore be possible to re-use ApplicationContextInitializers in the
Spring TestContext Framework as well.

This commit makes this possible at the @ContextConfiguration level by
allowing an array of ACI types to be specified, and the out-of-the-box
SmartContextLoader implementations invoke the declared initializers at
the appropriate time.

 - Added initializers and inheritInitializers attributes to
   @ContextConfiguration.

 - Introduced support for ApplicationContextInitializers in
   ContextConfigurationAttributes, MergedContextConfiguration, and
   ContextLoaderUtils.

 - MergedContextConfiguration stores context initializer classes as a
   Set and incorporates them into the implementations of hashCode() and
   equals() for proper context caching.

 - ApplicationContextInitializers are invoked in the new
   prepareContext(GenericApplicationContext, MergedContextConfiguration)
   method in AbstractGenericContextLoader, and ordering declared via the
   Ordered interface and @Order annotation is honored.

 - Updated DelegatingSmartContextLoader to support initializers.
   Specifically, a test class may optionally declare neither XML
   configuration files nor annotated classes and instead declare only
   application context initializers. In such cases, an attempt will
   still be made to detect defaults, but their absence will not result
   an an exception.

 - Documented support for application context initializers in Javadoc
   and in the testing chapter of the reference manual.

Issue: SPR-9011
This commit is contained in:
Sam Brannen 2012-08-09 19:05:08 +02:00
parent 9c8c967caa
commit 1f93777bbd
24 changed files with 1595 additions and 260 deletions

View File

@ -1,13 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beansProjectDescription>
<version>1</version>
<pluginVersion><![CDATA[2.6.0.201104111100-PATCH]]></pluginVersion>
<pluginVersion><![CDATA[3.0.0.201208090952-RELEASE]]></pluginVersion>
<configSuffixes>
<configSuffix><![CDATA[xml]]></configSuffix>
</configSuffixes>
<enableImports><![CDATA[false]]></enableImports>
<configs>
<config>src/test/java/org/springframework/test/context/junit4/profile/xml/DefaultProfileXmlConfigTests-context.xml</config>
<config>src/test/java/org/springframework/test/context/junit4/aci/xml/MultipleInitializersXmlConfigTests-context.xml</config>
</configs>
<configSets>
</configSets>

View File

@ -23,19 +23,19 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
/**
* {@code ContextConfiguration} defines class-level metadata that is
* {@code @ContextConfiguration} defines class-level metadata that is
* used to determine how to load and configure an
* {@link org.springframework.context.ApplicationContext ApplicationContext}
* for test classes.
* for integration tests.
*
* <h3>Supported Resource Types</h3>
*
* <p>Prior to Spring 3.1, only path-based resource locations were supported.
* As of Spring 3.1, {@link #loader context loaders} may choose to support
* As of Spring 3.1, {@linkplain #loader context loaders} may choose to support
* either path-based or class-based resources (but not both). Consequently
* {@code @ContextConfiguration} can be used to declare either path-based
* resource locations (via the {@link #locations} or {@link #value}
@ -47,16 +47,20 @@ import org.springframework.context.annotation.Configuration;
* <p>The term <em>annotated class</em> can refer to any of the following.
*
* <ul>
* <li>A class annotated with {@link Configuration @Configuration}</li>
* <li>A class annotated with
* {@link org.springframework.context.annotation.Configuration @Configuration}</li>
* <li>A component (i.e., a class annotated with
* {@link org.springframework.stereotype.Component @Component},
* {@link org.springframework.stereotype.Service @Service},
* {@link org.springframework.stereotype.Repository @Repository}, etc.)</li>
* <li>A JSR-330 compliant class that is annotated with {@code javax.inject} annotations</li>
* <li>Any other class that contains {@link Bean @Bean}-methods</li>
* <li>Any other class that contains
* {@link org.springframework.context.annotation.Bean @Bean}-methods</li>
* </ul>
*
* Consult the JavaDoc for {@link Configuration @Configuration} and {@link Bean @Bean}
* Consult the Javadoc for
* {@link org.springframework.context.annotation.Configuration @Configuration} and
* {@link org.springframework.context.annotation.Bean @Bean}
* for further information regarding the configuration and semantics of
* <em>annotated classes</em>.
*
@ -66,8 +70,8 @@ import org.springframework.context.annotation.Configuration;
* @see SmartContextLoader
* @see ContextConfigurationAttributes
* @see MergedContextConfiguration
* @see org.springframework.context.ApplicationContext
* @see ActiveProfiles
* @see org.springframework.context.ApplicationContext
*/
@Documented
@Inherited
@ -82,6 +86,7 @@ public @interface ContextConfiguration {
* with {@link #locations} or {@link #classes}, but it may be used
* instead of {@link #locations}.
* @since 3.0
* @see #inheritLocations
*/
String[] value() default {};
@ -111,6 +116,7 @@ public @interface ContextConfiguration {
* {@link #value} or {@link #classes}, but it may be used instead of
* {@link #value}.
* @since 2.5
* @see #inheritLocations
*/
String[] locations() default {};
@ -131,9 +137,31 @@ public @interface ContextConfiguration {
* @since 3.1
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.test.context.support.AnnotationConfigContextLoader
* @see #inheritLocations
*/
Class<?>[] classes() default {};
/**
* The application context <em>initializer classes</em> to use for initializing
* a {@link ConfigurableApplicationContext}.
*
* <p>The concrete {@code ConfigurableApplicationContext} type supported by each
* declared initializer must be compatible with the type of {@code ApplicationContext}
* created by the {@link SmartContextLoader} in use.
*
* <p>{@code SmartContextLoader} implementations typically detect whether
* Spring's {@link org.springframework.core.Ordered Ordered} interface has been
* implemented or if the @{@link org.springframework.core.annotation.Order Order}
* annotation is present and sort instances accordingly prior to invoking them.
*
* @since 3.2
* @see org.springframework.context.ApplicationContextInitializer
* @see org.springframework.context.ConfigurableApplicationContext
* @see #inheritInitializers
* @see #loader
*/
Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>[] initializers() default {};
/**
* Whether or not {@link #locations resource locations} or <em>annotated
* classes</em> from test superclasses should be <em>inherited</em>.
@ -194,7 +222,45 @@ public @interface ContextConfiguration {
boolean inheritLocations() default true;
/**
* The type of {@link ContextLoader} (or {@link SmartContextLoader}) to use
* Whether or not {@linkplain #initializers context initializers} from test
* superclasses should be <em>inherited</em>.
*
* <p>The default value is <code>true</code>. This means that an annotated
* class will <em>inherit</em> the application context initializers defined
* by test superclasses. Specifically, the initializers for a given test
* class will be added to the set of initializers defined by test
* superclasses. Thus, subclasses have the option of <em>extending</em> the
* set of initializers.
*
* <p>If <code>inheritInitializers</code> is set to <code>false</code>, the
* initializers for the annotated class will <em>shadow</em> and effectively
* replace any initializers defined by superclasses.
*
* <p>In the following example, the
* {@link org.springframework.context.ApplicationContext ApplicationContext}
* for {@code ExtendedTest} will be initialized using
* {@code BaseInitializer} <strong>and</strong> {@code ExtendedInitializer}.
* Note, however, that the order in which the initializers are invoked
* depends on whether they implement {@link org.springframework.core.Ordered
* Ordered} or are annotated with {@link org.springframework.core.annotation.Order
* &#064;Order}.
* <pre class="code">
* &#064;ContextConfiguration(initializers = BaseInitializer.class)
* public class BaseTest {
* // ...
* }
*
* &#064;ContextConfiguration(initializers = ExtendedInitializer.class)
* public class ExtendedTest extends BaseTest {
* // ...
* }
* </pre>
* @since 3.2
*/
boolean inheritInitializers() default true;
/**
* The type of {@link SmartContextLoader} (or {@link ContextLoader}) to use
* for loading an {@link org.springframework.context.ApplicationContext
* ApplicationContext}.
*

View File

@ -18,12 +18,15 @@ package org.springframework.test.context;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* <code>ContextConfigurationAttributes</code> encapsulates the context
* {@code ContextConfigurationAttributes} encapsulates the context
* configuration attributes declared on a test class via
* {@link ContextConfiguration @ContextConfiguration}.
*
@ -47,6 +50,10 @@ public class ContextConfigurationAttributes {
private final Class<? extends ContextLoader> contextLoaderClass;
private final Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>[] initializers;
private final boolean inheritInitializers;
/**
* Resolve resource locations from the {@link ContextConfiguration#locations() locations}
@ -68,8 +75,7 @@ public class ContextConfigurationAttributes {
ObjectUtils.nullSafeToString(valueLocations), ObjectUtils.nullSafeToString(locations));
logger.error(msg);
throw new IllegalStateException(msg);
}
else if (!ObjectUtils.isEmpty(valueLocations)) {
} else if (!ObjectUtils.isEmpty(valueLocations)) {
locations = valueLocations;
}
@ -79,31 +85,59 @@ public class ContextConfigurationAttributes {
/**
* Construct a new {@link ContextConfigurationAttributes} instance for the
* supplied {@link ContextConfiguration @ContextConfiguration} annotation and
* the {@link Class test class} that declared it.
* the {@linkplain Class test class} that declared it.
* @param declaringClass the test class that declared {@code @ContextConfiguration}
* @param contextConfiguration the annotation from which to retrieve the attributes
*/
public ContextConfigurationAttributes(Class<?> declaringClass, ContextConfiguration contextConfiguration) {
this(declaringClass, resolveLocations(declaringClass, contextConfiguration), contextConfiguration.classes(),
contextConfiguration.inheritLocations(), contextConfiguration.loader());
contextConfiguration.inheritLocations(), contextConfiguration.initializers(),
contextConfiguration.inheritInitializers(), contextConfiguration.loader());
}
/**
* Construct a new {@link ContextConfigurationAttributes} instance for the
* {@link Class test class} that declared the
* {@linkplain Class test class} that declared the
* {@link ContextConfiguration @ContextConfiguration} annotation and its
* corresponding attributes.
*
* @param declaringClass the test class that declared {@code @ContextConfiguration}
* @param locations the resource locations declared via {@code @ContextConfiguration}
* @param classes the annotated classes declared via {@code @ContextConfiguration}
* @param inheritLocations the <code>inheritLocations</code> flag declared via {@code @ContextConfiguration}
* @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</code>, or if the {@code locations} and {@code classes} are both non-empty
* {@code null}, or if the {@code locations} and {@code classes} are both non-empty
* @deprecated as of Spring 3.2, use
* {@link #ContextConfigurationAttributes(Class, String[], Class[], boolean, Class[], boolean, Class)}
* instead
*/
@Deprecated
public ContextConfigurationAttributes(Class<?> declaringClass, String[] locations, Class<?>[] classes,
boolean inheritLocations, Class<? extends ContextLoader> contextLoaderClass) {
this(declaringClass, locations, classes, inheritLocations, null, true, contextLoaderClass);
}
/**
* Construct a new {@link ContextConfigurationAttributes} instance for the
* {@linkplain Class test class} that declared the
* {@link ContextConfiguration @ContextConfiguration} annotation and its
* corresponding attributes.
*
* @param declaringClass the test class that declared {@code @ContextConfiguration}
* @param locations the resource locations declared via {@code @ContextConfiguration}
* @param classes the annotated classes declared via {@code @ContextConfiguration}
* @param inheritLocations the {@code inheritLocations} flag declared via {@code @ContextConfiguration}
* @param initializers the context initializers declared via {@code @ContextConfiguration}
* @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
*/
public ContextConfigurationAttributes(Class<?> declaringClass, String[] locations, Class<?>[] classes,
boolean inheritLocations,
Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>[] initializers,
boolean inheritInitializers, Class<? extends ContextLoader> contextLoaderClass) {
Assert.notNull(declaringClass, "declaringClass must not be null");
Assert.notNull(contextLoaderClass, "contextLoaderClass must not be null");
@ -122,14 +156,16 @@ public class ContextConfigurationAttributes {
this.locations = locations;
this.classes = classes;
this.inheritLocations = inheritLocations;
this.initializers = initializers;
this.inheritInitializers = inheritInitializers;
this.contextLoaderClass = contextLoaderClass;
}
/**
* Get the {@link Class class} that declared the
* Get the {@linkplain Class class} that declared the
* {@link ContextConfiguration @ContextConfiguration} annotation.
*
* @return the declaring class; never <code>null</code>
* @return the declaring class; never {@code null}
*/
public Class<?> getDeclaringClass() {
return declaringClass;
@ -143,7 +179,7 @@ public class ContextConfigurationAttributes {
* represent a <em>processed</em> value that does not match the original value
* declared via {@link ContextConfiguration @ContextConfiguration}.
*
* @return the resource locations; potentially <code>null</code> or <em>empty</em>
* @return the resource locations; potentially {@code null} or <em>empty</em>
* @see ContextConfiguration#value
* @see ContextConfiguration#locations
* @see #setLocations(String[])
@ -170,7 +206,7 @@ public class ContextConfigurationAttributes {
* represent a <em>processed</em> value that does not match the original value
* declared via {@link ContextConfiguration @ContextConfiguration}.
*
* @return the annotated classes; potentially <code>null</code> or <em>empty</em>
* @return the annotated classes; potentially {@code null} or <em>empty</em>
* @see ContextConfiguration#classes
* @see #setClasses(Class[])
*/
@ -192,7 +228,7 @@ public class ContextConfigurationAttributes {
* Determine if this {@code ContextConfigurationAttributes} instance has
* path-based resource locations.
*
* @return <code>true</code> if the {@link #getLocations() locations} array is not empty
* @return {@code true} if the {@link #getLocations() locations} array is not empty
* @see #hasResources()
* @see #hasClasses()
*/
@ -204,7 +240,7 @@ public class ContextConfigurationAttributes {
* Determine if this {@code ContextConfigurationAttributes} instance has
* class-based resources.
*
* @return <code>true</code> if the {@link #getClasses() classes} array is not empty
* @return {@code true} if the {@link #getClasses() classes} array is not empty
* @see #hasResources()
* @see #hasLocations()
*/
@ -216,7 +252,7 @@ public class ContextConfigurationAttributes {
* Determine if this {@code ContextConfigurationAttributes} instance has
* either path-based resource locations or class-based resources.
*
* @return <code>true</code> if either the {@link #getLocations() locations}
* @return {@code true} if either the {@link #getLocations() locations}
* or the {@link #getClasses() classes} array is not empty
* @see #hasLocations()
* @see #hasClasses()
@ -226,10 +262,10 @@ public class ContextConfigurationAttributes {
}
/**
* Get the <code>inheritLocations</code> flag that was declared via
* Get the {@code inheritLocations} flag that was declared via
* {@link ContextConfiguration @ContextConfiguration}.
*
* @return the <code>inheritLocations</code> flag
* @return the {@code inheritLocations} flag
* @see ContextConfiguration#inheritLocations
*/
public boolean isInheritLocations() {
@ -237,10 +273,32 @@ public class ContextConfigurationAttributes {
}
/**
* Get the <code>ContextLoader</code> class that was declared via
* Get the {@code ApplicationContextInitializer} classes that were declared via
* {@link ContextConfiguration @ContextConfiguration}.
*
* @return the <code>ContextLoader</code> class
* @return the {@code ApplicationContextInitializer} classes
* @since 3.2
*/
public Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>[] getInitializers() {
return initializers;
}
/**
* Get the {@code inheritInitializers} flag that was declared via
* {@link ContextConfiguration @ContextConfiguration}.
*
* @return the {@code inheritInitializers} flag
* @since 3.2
*/
public boolean isInheritInitializers() {
return inheritInitializers;
}
/**
* Get the {@code ContextLoader} class that was declared via
* {@link ContextConfiguration @ContextConfiguration}.
*
* @return the {@code ContextLoader} class
* @see ContextConfiguration#loader
*/
public Class<? extends ContextLoader> getContextLoaderClass() {
@ -258,6 +316,8 @@ public class ContextConfigurationAttributes {
.append("locations", ObjectUtils.nullSafeToString(locations))//
.append("classes", ObjectUtils.nullSafeToString(classes))//
.append("inheritLocations", inheritLocations)//
.append("initializers", ObjectUtils.nullSafeToString(initializers))//
.append("inheritInitializers", inheritInitializers)//
.append("contextLoaderClass", contextLoaderClass.getName())//
.toString();
}

View File

@ -23,8 +23,8 @@ import org.springframework.context.ApplicationContext;
* for an integration test managed by the Spring TestContext Framework.
*
* <p><b>Note</b>: as of Spring 3.1, implement {@link SmartContextLoader} instead
* of this interface in order to provide support for annotated classes and active
* bean definition profiles.
* of this interface in order to provide support for annotated classes, active
* bean definition profiles, and application context initializers.
*
* <p>Clients of a ContextLoader should call
* {@link #processLocations(Class,String...) processLocations()} prior to

View File

@ -21,12 +21,15 @@ import static org.springframework.core.annotation.AnnotationUtils.findAnnotation
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
@ -156,8 +159,7 @@ abstract class ContextLoaderUtils {
}
return (Class<? extends ContextLoader>) ContextLoaderUtils.class.getClassLoader().loadClass(
defaultContextLoaderClassName);
}
catch (ClassNotFoundException ex) {
} catch (ClassNotFoundException ex) {
throw new IllegalStateException("Could not load default ContextLoader class ["
+ defaultContextLoaderClassName + "]. Specify @ContextConfiguration's 'loader' "
+ "attribute or make the default loader class available.");
@ -169,17 +171,15 @@ abstract class ContextLoaderUtils {
* attributes} for the supplied {@link Class class} and its superclasses.
*
* <p>Note that the {@link ContextConfiguration#inheritLocations
* inheritLocations} flag of {@link ContextConfiguration
* &#064;ContextConfiguration} will be taken into consideration.
* Specifically, if the <code>inheritLocations</code> flag is set to
* <code>true</code>, configuration attributes defined in the test
* class will be appended to the configuration attributes defined in
* superclasses.
* inheritLocations} and {@link ContextConfiguration#inheritInitializers()
* inheritInitializers} flags of {@link ContextConfiguration
* &#064;ContextConfiguration} will <strong>not</strong> be taken into
* consideration. If these flags need to be honored, that must be handled
* manually when traversing the list returned by this method.
*
* @param clazz the class for which to resolve the configuration attributes (must
* not be <code>null</code>)
* @return the list of configuration attributes for the specified class,
* including configuration attributes from superclasses if appropriate
* @return the list of configuration attributes for the specified class
* (never <code>null</code>)
* @throws IllegalArgumentException if the supplied class is <code>null</code> or
* if {@code @ContextConfiguration} is not <em>present</em> on the supplied class
@ -211,13 +211,69 @@ abstract class ContextLoaderUtils {
attributesList.add(0, attributes);
declaringClass = contextConfiguration.inheritLocations() ? findAnnotationDeclaringClass(annotationType,
declaringClass.getSuperclass()) : null;
declaringClass = findAnnotationDeclaringClass(annotationType, declaringClass.getSuperclass());
}
return attributesList;
}
/**
* Create a copy of the supplied list of {@code ContextConfigurationAttributes}
* in reverse order.
*
* @since 3.2
*/
private static List<ContextConfigurationAttributes> reverseContextConfigurationAttributes(
List<ContextConfigurationAttributes> configAttributesList) {
List<ContextConfigurationAttributes> configAttributesListReversed = new ArrayList<ContextConfigurationAttributes>(
configAttributesList);
Collections.reverse(configAttributesListReversed);
return configAttributesListReversed;
}
/**
* Resolve the list of merged {@code ApplicationContextInitializer} classes
* for the supplied list of {@code ContextConfigurationAttributes}.
*
* <p>Note that the {@link ContextConfiguration#inheritInitializers inheritInitializers}
* flag of {@link ContextConfiguration @ContextConfiguration} will be taken into
* consideration. Specifically, if the <code>inheritInitializers</code> flag is
* set to <code>true</code> for a given level in the class hierarchy represented by
* the provided configuration attributes, context initializer classes defined
* at the given level will be merged with those defined in higher levels
* of the class hierarchy.
*
* @param configAttributesList the list of configuration attributes to process
* (must not be <code>null</code>)
* @return the list of merged context initializer classes, including those
* from superclasses if appropriate (never <code>null</code>)
* @since 3.2
*/
static Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> resolveInitializerClasses(
List<ContextConfigurationAttributes> configAttributesList) {
Assert.notNull(configAttributesList, "configAttributesList must not be null");
final Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses = //
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
// Traverse config attributes in reverse order (i.e., as if we were traversing up
// the class hierarchy).
for (ContextConfigurationAttributes configAttributes : reverseContextConfigurationAttributes(configAttributesList)) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Processing context initializers for context configuration attributes %s",
configAttributes));
}
initializerClasses.addAll(Arrays.asList(configAttributes.getInitializers()));
if (!configAttributes.isInheritInitializers()) {
break;
}
}
return initializerClasses;
}
/**
* Resolve <em>active bean definition profiles</em> for the supplied {@link Class}.
*
@ -266,8 +322,7 @@ abstract class ContextLoaderUtils {
ObjectUtils.nullSafeToString(valueProfiles), ObjectUtils.nullSafeToString(profiles));
logger.error(msg);
throw new IllegalStateException(msg);
}
else if (!ObjectUtils.isEmpty(valueProfiles)) {
} else if (!ObjectUtils.isEmpty(valueProfiles)) {
profiles = valueProfiles;
}
@ -309,31 +364,38 @@ abstract class ContextLoaderUtils {
final List<String> locationsList = new ArrayList<String>();
final List<Class<?>> classesList = new ArrayList<Class<?>>();
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
// Traverse config attributes in reverse order (i.e., as if we were traversing up
// the class hierarchy).
for (ContextConfigurationAttributes configAttributes : reverseContextConfigurationAttributes(configAttributesList)) {
if (logger.isTraceEnabled()) {
logger.trace(String.format(
"Processing locations and classes for context configuration attributes [%s]", configAttributes));
logger.trace(String.format("Processing locations and classes for context configuration attributes %s",
configAttributes));
}
if (contextLoader instanceof SmartContextLoader) {
SmartContextLoader smartContextLoader = (SmartContextLoader) contextLoader;
smartContextLoader.processContextConfiguration(configAttributes);
locationsList.addAll(Arrays.asList(configAttributes.getLocations()));
classesList.addAll(Arrays.asList(configAttributes.getClasses()));
}
else {
locationsList.addAll(0, Arrays.asList(configAttributes.getLocations()));
classesList.addAll(0, Arrays.asList(configAttributes.getClasses()));
} else {
String[] processedLocations = contextLoader.processLocations(configAttributes.getDeclaringClass(),
configAttributes.getLocations());
locationsList.addAll(Arrays.asList(processedLocations));
locationsList.addAll(0, Arrays.asList(processedLocations));
// Legacy ContextLoaders don't know how to process classes
}
if (!configAttributes.isInheritLocations()) {
break;
}
}
String[] locations = StringUtils.toStringArray(locationsList);
Class<?>[] classes = ClassUtils.toClassArray(classesList);
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses = resolveInitializerClasses(configAttributesList);
String[] activeProfiles = resolveActiveProfiles(testClass);
return new MergedContextConfiguration(testClass, locations, classes, activeProfiles, contextLoader);
return new MergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
contextLoader);
}
}

View File

@ -18,9 +18,13 @@ package org.springframework.test.context;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@ -59,10 +63,13 @@ public class MergedContextConfiguration implements Serializable {
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
private static final Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> EMPTY_INITIALIZER_CLASSES = //
Collections.<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> emptySet();
private final Class<?> testClass;
private final String[] locations;
private final Class<?>[] classes;
private final Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses;
private final String[] activeProfiles;
private final ContextLoader contextLoader;
@ -75,6 +82,12 @@ public class MergedContextConfiguration implements Serializable {
return classes == null ? EMPTY_CLASS_ARRAY : classes;
}
private static Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> processContextInitializerClasses(
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses) {
return contextInitializerClasses == null ? EMPTY_INITIALIZER_CLASSES
: Collections.unmodifiableSet(contextInitializerClasses);
}
private static String[] processActiveProfiles(String[] activeProfiles) {
if (activeProfiles == null) {
return EMPTY_STRING_ARRAY;
@ -111,46 +124,84 @@ public class MergedContextConfiguration implements Serializable {
* @param classes the merged annotated classes
* @param activeProfiles the merged active bean definition profiles
* @param contextLoader the resolved <code>ContextLoader</code>
* @see #MergedContextConfiguration(Class, String[], Class[], Set, String[], ContextLoader)
*/
public MergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
String[] activeProfiles, ContextLoader contextLoader) {
this(testClass, locations, classes, null, activeProfiles, contextLoader);
}
/**
* Create a new {@code MergedContextConfiguration} instance for the
* supplied test class, resource locations, annotated classes, context
* initializers, active profiles, and {@code ContextLoader}.
*
* <p>If a <code>null</code> value is supplied for <code>locations</code>,
* <code>classes</code>, or <code>activeProfiles</code> an empty array will
* be stored instead. If a <code>null</code> value is supplied for the
* <code>contextInitializerClasses</code> an empty set will be stored instead.
* Furthermore, active profiles will be sorted, and duplicate profiles will
* be removed.
*
* @param testClass the test class for which the configuration was merged
* @param locations the merged resource locations
* @param classes the merged annotated classes
* @param contextInitializerClasses the merged context initializer classes
* @param activeProfiles the merged active bean definition profiles
* @param contextLoader the resolved <code>ContextLoader</code>
*/
public MergedContextConfiguration(
Class<?> testClass,
String[] locations,
Class<?>[] classes,
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
String[] activeProfiles, ContextLoader contextLoader) {
this.testClass = testClass;
this.locations = processLocations(locations);
this.classes = processClasses(classes);
this.contextInitializerClasses = processContextInitializerClasses(contextInitializerClasses);
this.activeProfiles = processActiveProfiles(activeProfiles);
this.contextLoader = contextLoader;
}
/**
* Get the {@link Class test class} associated with this {@code MergedContextConfiguration}.
* Get the {@linkplain Class test class} associated with this {@code MergedContextConfiguration}.
*/
public Class<?> getTestClass() {
return testClass;
}
/**
* Get the merged resource locations for the {@link #getTestClass() test class}.
* Get the merged resource locations for the {@linkplain #getTestClass() test class}.
*/
public String[] getLocations() {
return locations;
}
/**
* Get the merged annotated classes for the {@link #getTestClass() test class}.
* Get the merged annotated classes for the {@linkplain #getTestClass() test class}.
*/
public Class<?>[] getClasses() {
return classes;
}
/**
* Get the merged active bean definition profiles for the {@link #getTestClass() test class}.
* Get the merged {@code ApplicationContextInitializer} classes for the
* {@linkplain #getTestClass() test class}.
*/
public Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> getContextInitializerClasses() {
return contextInitializerClasses;
}
/**
* Get the merged active bean definition profiles for the {@linkplain #getTestClass() test class}.
*/
public String[] getActiveProfiles() {
return activeProfiles;
}
/**
* Get the resolved {@link ContextLoader} for the {@link #getTestClass() test class}.
* Get the resolved {@link ContextLoader} for the {@linkplain #getTestClass() test class}.
*/
public ContextLoader getContextLoader() {
return contextLoader;
@ -159,7 +210,7 @@ public class MergedContextConfiguration implements Serializable {
/**
* Generate a unique hash code for all properties of this
* {@code MergedContextConfiguration} excluding the
* {@link #getTestClass() test class}.
* {@linkplain #getTestClass() test class}.
*/
@Override
public int hashCode() {
@ -167,6 +218,7 @@ public class MergedContextConfiguration implements Serializable {
int result = 1;
result = prime * result + Arrays.hashCode(locations);
result = prime * result + Arrays.hashCode(classes);
result = prime * result + contextInitializerClasses.hashCode();
result = prime * result + Arrays.hashCode(activeProfiles);
result = prime * result + nullSafeToString(contextLoader).hashCode();
return result;
@ -174,10 +226,11 @@ public class MergedContextConfiguration implements Serializable {
/**
* Determine if the supplied object is equal to this {@code MergedContextConfiguration}
* instance by comparing both object's {@link #getLocations() locations},
* {@link #getClasses() annotated classes}, {@link #getActiveProfiles()
* active profiles}, and the fully qualified names of their
* {@link #getContextLoader() ContextLoaders}.
* instance by comparing both object's {@linkplain #getLocations() locations},
* {@linkplain #getClasses() annotated classes},
* {@linkplain #getContextInitializerClasses() context initializer classes},
* {@linkplain #getActiveProfiles() active profiles}, and the fully qualified
* names of their {@link #getContextLoader() ContextLoaders}.
*/
@Override
public boolean equals(Object obj) {
@ -197,6 +250,9 @@ public class MergedContextConfiguration implements Serializable {
if (!Arrays.equals(this.classes, that.classes)) {
return false;
}
if (!this.contextInitializerClasses.equals(that.contextInitializerClasses)) {
return false;
}
if (!Arrays.equals(this.activeProfiles, that.activeProfiles)) {
return false;
}
@ -208,9 +264,10 @@ public class MergedContextConfiguration implements Serializable {
}
/**
* Provide a String representation of the {@link #getTestClass() test class},
* {@link #getLocations() locations}, {@link #getClasses() annotated classes},
* {@link #getActiveProfiles() active profiles}, and the name of the
* Provide a String representation of the {@linkplain #getTestClass() test class},
* {@linkplain #getLocations() locations}, {@linkplain #getClasses() annotated classes},
* {@linkplain #getContextInitializerClasses() context initializer classes},
* {@linkplain #getActiveProfiles() active profiles}, and the name of the
* {@link #getContextLoader() ContextLoader}.
*/
@Override
@ -219,6 +276,7 @@ public class MergedContextConfiguration implements Serializable {
.append("testClass", testClass)//
.append("locations", ObjectUtils.nullSafeToString(locations))//
.append("classes", ObjectUtils.nullSafeToString(classes))//
.append("contextInitializerClasses", ObjectUtils.nullSafeToString(contextInitializerClasses))//
.append("activeProfiles", ObjectUtils.nullSafeToString(activeProfiles))//
.append("contextLoader", nullSafeToString(contextLoader))//
.toString();

View File

@ -16,14 +16,25 @@
package org.springframework.test.context.support;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
@ -66,11 +77,8 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
*
* <ul>
* <li>Creates a {@link GenericApplicationContext} instance.</li>
* <li>Sets the <em>active bean definition profiles</em> from the supplied
* <code>MergedContextConfiguration</code> in the
* {@link org.springframework.core.env.Environment Environment} of the context.</li>
* <li>Calls {@link #prepareContext(GenericApplicationContext)} to allow for customizing the context
* before bean definitions are loaded.</li>
* <li>Calls {@link #prepareContext(GenericApplicationContext, MergedContextConfiguration)}
* to allow for customizing the context before bean definitions are loaded.</li>
* <li>Calls {@link #customizeBeanFactory(DefaultListableBeanFactory)} to allow for customizing the
* context's <code>DefaultListableBeanFactory</code>.</li>
* <li>Delegates to {@link #loadBeanDefinitions(GenericApplicationContext, MergedContextConfiguration)}
@ -95,9 +103,9 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
logger.debug(String.format("Loading ApplicationContext for merged context configuration [%s].",
mergedConfig));
}
GenericApplicationContext context = new GenericApplicationContext();
context.getEnvironment().setActiveProfiles(mergedConfig.getActiveProfiles());
prepareContext(context);
prepareContext(context, mergedConfig);
customizeBeanFactory(context.getDefaultListableBeanFactory());
loadBeanDefinitions(context, mergedConfig);
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
@ -132,6 +140,7 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
*
* <p><b>Note</b>: this method does not provide a means to set active bean definition
* profiles for the loaded context. See {@link #loadContext(MergedContextConfiguration)}
* and {@link #prepareContext(GenericApplicationContext, MergedContextConfiguration)}
* for an alternative.
*
* @return a new application context
@ -174,6 +183,72 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
protected void prepareContext(GenericApplicationContext context) {
}
/**
* Prepare the {@link GenericApplicationContext} created by this
* {@code SmartContextLoader} <i>before</i> bean definitions are read.
*
* <p>The default implementation:
* <ul>
* <li>Calls {@link #prepareContext(GenericApplicationContext)} for backwards
* compatibility with the {@link org.springframework.test.context.ContextLoader
* ContextLoader} SPI.</li>
* <li>Sets the <em>active bean definition profiles</em> from the supplied
* <code>MergedContextConfiguration</code> in the
* {@link org.springframework.core.env.Environment Environment} of the context.</li>
* <li>Determines what (if any) context initializer classes have been supplied
* via the {@code MergedContextConfiguration} and
* {@linkplain ApplicationContextInitializer#initialize invokes each} with the
* given application context.</li>
* </ul>
*
* <p>Any {@code ApplicationContextInitializers} implementing
* {@link org.springframework.core.Ordered Ordered} or marked with {@link
* org.springframework.core.annotation.Order @Order} will be sorted appropriately.
*
* @param applicationContext the newly created application context
* @param mergedConfig the merged context configuration
* @see ApplicationContextInitializer#initialize(GenericApplicationContext)
* @see #loadContext(MergedContextConfiguration)
* @see GenericApplicationContext#setAllowBeanDefinitionOverriding
* @see GenericApplicationContext#setResourceLoader
* @see GenericApplicationContext#setId
* @since 3.2
*/
@SuppressWarnings("unchecked")
protected void prepareContext(GenericApplicationContext applicationContext,
MergedContextConfiguration mergedConfig) {
prepareContext(applicationContext);
applicationContext.getEnvironment().setActiveProfiles(mergedConfig.getActiveProfiles());
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses = mergedConfig.getContextInitializerClasses();
if (initializerClasses.size() == 0) {
// no ApplicationContextInitializers have been declared -> nothing to do
return;
}
final List<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances = new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
final Class<?> contextClass = applicationContext.getClass();
for (Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>> initializerClass : initializerClasses) {
Class<?> initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass,
ApplicationContextInitializer.class);
Assert.isAssignable(initializerContextClass, contextClass, String.format(
"Could not add context initializer [%s] since its generic parameter [%s] "
+ "is not assignable from the type of application context used by this "
+ "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
contextClass.getName()));
initializerInstances.add((ApplicationContextInitializer<ConfigurableApplicationContext>) BeanUtils.instantiateClass(initializerClass));
}
Collections.sort(initializerInstances, new AnnotationAwareOrderComparator());
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
initializer.initialize(applicationContext);
}
}
/**
* Customize the internal bean factory of the ApplicationContext created by
* this <code>ContextLoader</code>.

View File

@ -21,6 +21,7 @@ import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextConfigurationAttributes;
@ -50,6 +51,12 @@ import org.springframework.util.ObjectUtils;
* the default loader, thus providing automatic support for either XML configuration
* files or annotated classes, but not both simultaneously.
*
* <p>As of Spring 3.2, a test class may optionally declare neither XML configuration
* files nor annotated classes and instead declare only {@linkplain
* ContextConfiguration#initializers application context initializers}. In such
* cases, an attempt will still be made to detect defaults, but their absence will
* not result an an exception.
*
* @author Sam Brannen
* @since 3.1
* @see SmartContextLoader
@ -78,11 +85,18 @@ public class DelegatingSmartContextLoader implements SmartContextLoader {
loader.processContextConfiguration(configAttributes);
}
private static ApplicationContext delegateLoading(SmartContextLoader loader, MergedContextConfiguration mergedConfig)
throws Exception {
if (logger.isDebugEnabled()) {
logger.debug(String.format("Delegating to %s to load context from %s.", name(loader), mergedConfig));
}
return loader.loadContext(mergedConfig);
}
private static boolean supports(SmartContextLoader loader, MergedContextConfiguration mergedConfig) {
if (loader instanceof AnnotationConfigContextLoader) {
return ObjectUtils.isEmpty(mergedConfig.getLocations()) && !ObjectUtils.isEmpty(mergedConfig.getClasses());
}
else {
} else {
return !ObjectUtils.isEmpty(mergedConfig.getLocations()) && ObjectUtils.isEmpty(mergedConfig.getClasses());
}
}
@ -132,11 +146,9 @@ public class DelegatingSmartContextLoader implements SmartContextLoader {
// appropriate loader process the configuration.
if (configAttributes.hasLocations()) {
delegateProcessing(xmlLoader, configAttributes);
}
else if (configAttributes.hasClasses()) {
} else if (configAttributes.hasClasses()) {
delegateProcessing(annotationConfigLoader, configAttributes);
}
else {
} else {
// Else attempt to detect defaults...
// Let the XML loader process the configuration.
@ -173,10 +185,12 @@ public class DelegatingSmartContextLoader implements SmartContextLoader {
name(annotationConfigLoader), configAttributes));
}
// If neither loader detected defaults, throw an exception.
if (!configAttributes.hasResources()) {
// If neither loader detected defaults and no initializers were declared,
// throw an exception.
if (!configAttributes.hasResources() && ObjectUtils.isEmpty(configAttributes.getInitializers())) {
throw new IllegalStateException(String.format(
"Neither %s nor %s was able to detect defaults for context configuration %s.", name(xmlLoader),
"Neither %s nor %s was able to detect defaults, and no ApplicationContextInitializers "
+ "were declared for context configuration %s", name(xmlLoader),
name(annotationConfigLoader), configAttributes));
}
@ -219,16 +233,19 @@ public class DelegatingSmartContextLoader implements SmartContextLoader {
List<SmartContextLoader> candidates = Arrays.asList(xmlLoader, annotationConfigLoader);
for (SmartContextLoader loader : candidates) {
// Determine if each loader can load a context from the
// mergedConfig. If it can, let it; otherwise, keep iterating.
// Determine if each loader can load a context from the mergedConfig. If it
// can, let it; otherwise, keep iterating.
if (supports(loader, mergedConfig)) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("Delegating to %s to load context from %s.", name(loader), mergedConfig));
}
return loader.loadContext(mergedConfig);
return delegateLoading(loader, mergedConfig);
}
}
// If neither of the candidates supports the mergedConfig based on resources but
// ACIs were declared, then delegate to the ACCL.
if (!mergedConfig.getContextInitializerClasses().isEmpty()) {
return delegateLoading(annotationConfigLoader, mergedConfig);
}
throw new IllegalStateException(String.format(
"Neither %s nor %s was able to load an ApplicationContext from %s.", name(xmlLoader),
name(annotationConfigLoader), mergedConfig));

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 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.
@ -16,19 +16,24 @@
package org.springframework.test.context;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.context.ContextLoaderUtils.*;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.junit.Test;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.test.context.support.DelegatingSmartContextLoader;
import org.springframework.test.context.support.GenericPropertiesContextLoader;
import org.springframework.web.context.support.GenericWebApplicationContext;
/**
* Unit tests for {@link ContextLoaderUtils}.
@ -40,6 +45,8 @@ public class ContextLoaderUtilsTests {
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> EMPTY_INITIALIZER_CLASSES = //
Collections.<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> emptySet();
private void assertAttributes(ContextConfigurationAttributes attributes, Class<?> expectedDeclaringClass,
@ -72,9 +79,20 @@ public class ContextLoaderUtilsTests {
AnnotationConfigContextLoader.class, true);
}
private void assertMergedContextConfiguration(MergedContextConfiguration mergedConfig, Class<?> expectedTestClass,
private void assertMergedConfig(MergedContextConfiguration mergedConfig, Class<?> expectedTestClass,
String[] expectedLocations, Class<?>[] expectedClasses,
Class<? extends ContextLoader> expectedContextLoaderClass) {
assertMergedConfig(mergedConfig, expectedTestClass, expectedLocations, expectedClasses,
EMPTY_INITIALIZER_CLASSES, expectedContextLoaderClass);
}
private void assertMergedConfig(
MergedContextConfiguration mergedConfig,
Class<?> expectedTestClass,
String[] expectedLocations,
Class<?>[] expectedClasses,
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> expectedInitializerClasses,
Class<? extends ContextLoader> expectedContextLoaderClass) {
assertNotNull(mergedConfig);
assertEquals(expectedTestClass, mergedConfig.getTestClass());
assertNotNull(mergedConfig.getLocations());
@ -83,16 +101,18 @@ public class ContextLoaderUtilsTests {
assertArrayEquals(expectedClasses, mergedConfig.getClasses());
assertNotNull(mergedConfig.getActiveProfiles());
assertEquals(expectedContextLoaderClass, mergedConfig.getContextLoader().getClass());
assertNotNull(mergedConfig.getContextInitializerClasses());
assertEquals(expectedInitializerClasses, mergedConfig.getContextInitializerClasses());
}
@Test(expected = IllegalStateException.class)
public void resolveContextConfigurationAttributesWithConflictingLocations() {
ContextLoaderUtils.resolveContextConfigurationAttributes(ConflictingLocations.class);
public void resolveConfigAttributesWithConflictingLocations() {
resolveContextConfigurationAttributes(ConflictingLocations.class);
}
@Test
public void resolveContextConfigurationAttributesWithBareAnnotations() {
List<ContextConfigurationAttributes> attributesList = ContextLoaderUtils.resolveContextConfigurationAttributes(BareAnnotations.class);
public void resolveConfigAttributesWithBareAnnotations() {
List<ContextConfigurationAttributes> attributesList = resolveContextConfigurationAttributes(BareAnnotations.class);
assertNotNull(attributesList);
assertEquals(1, attributesList.size());
assertAttributes(attributesList.get(0), BareAnnotations.class, EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY,
@ -100,24 +120,24 @@ public class ContextLoaderUtilsTests {
}
@Test
public void resolveContextConfigurationAttributesWithLocalAnnotationAndLocations() {
List<ContextConfigurationAttributes> attributesList = ContextLoaderUtils.resolveContextConfigurationAttributes(LocationsFoo.class);
public void resolveConfigAttributesWithLocalAnnotationAndLocations() {
List<ContextConfigurationAttributes> attributesList = resolveContextConfigurationAttributes(LocationsFoo.class);
assertNotNull(attributesList);
assertEquals(1, attributesList.size());
assertLocationsFooAttributes(attributesList.get(0));
}
@Test
public void resolveContextConfigurationAttributesWithLocalAnnotationAndClasses() {
List<ContextConfigurationAttributes> attributesList = ContextLoaderUtils.resolveContextConfigurationAttributes(ClassesFoo.class);
public void resolveConfigAttributesWithLocalAnnotationAndClasses() {
List<ContextConfigurationAttributes> attributesList = resolveContextConfigurationAttributes(ClassesFoo.class);
assertNotNull(attributesList);
assertEquals(1, attributesList.size());
assertClassesFooAttributes(attributesList.get(0));
}
@Test
public void resolveContextConfigurationAttributesWithLocalAndInheritedAnnotationsAndLocations() {
List<ContextConfigurationAttributes> attributesList = ContextLoaderUtils.resolveContextConfigurationAttributes(LocationsBar.class);
public void resolveConfigAttributesWithLocalAndInheritedAnnotationsAndLocations() {
List<ContextConfigurationAttributes> attributesList = resolveContextConfigurationAttributes(LocationsBar.class);
assertNotNull(attributesList);
assertEquals(2, attributesList.size());
assertLocationsFooAttributes(attributesList.get(0));
@ -125,8 +145,8 @@ public class ContextLoaderUtilsTests {
}
@Test
public void resolveContextConfigurationAttributesWithLocalAndInheritedAnnotationsAndClasses() {
List<ContextConfigurationAttributes> attributesList = ContextLoaderUtils.resolveContextConfigurationAttributes(ClassesBar.class);
public void resolveConfigAttributesWithLocalAndInheritedAnnotationsAndClasses() {
List<ContextConfigurationAttributes> attributesList = resolveContextConfigurationAttributes(ClassesBar.class);
assertNotNull(attributesList);
assertEquals(2, attributesList.size());
assertClassesFooAttributes(attributesList.get(0));
@ -134,16 +154,16 @@ public class ContextLoaderUtilsTests {
}
@Test(expected = IllegalArgumentException.class)
public void buildMergedContextConfigurationWithoutAnnotation() {
ContextLoaderUtils.buildMergedContextConfiguration(Enigma.class, null);
public void buildMergedConfigWithoutAnnotation() {
buildMergedContextConfiguration(Enigma.class, null);
}
@Test
public void buildMergedContextConfigurationWithBareAnnotations() {
public void buildMergedConfigWithBareAnnotations() {
Class<BareAnnotations> testClass = BareAnnotations.class;
MergedContextConfiguration mergedConfig = ContextLoaderUtils.buildMergedContextConfiguration(testClass, null);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
assertMergedContextConfiguration(
assertMergedConfig(
mergedConfig,
testClass,
new String[] { "classpath:/org/springframework/test/context/ContextLoaderUtilsTests$BareAnnotations-context.xml" },
@ -151,86 +171,159 @@ public class ContextLoaderUtilsTests {
}
@Test
public void buildMergedContextConfigurationWithLocalAnnotationAndLocations() {
public void buildMergedConfigWithLocalAnnotationAndLocations() {
Class<?> testClass = LocationsFoo.class;
MergedContextConfiguration mergedConfig = ContextLoaderUtils.buildMergedContextConfiguration(testClass, null);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
assertMergedContextConfiguration(mergedConfig, testClass, new String[] { "classpath:/foo.xml" },
EMPTY_CLASS_ARRAY, DelegatingSmartContextLoader.class);
assertMergedConfig(mergedConfig, testClass, new String[] { "classpath:/foo.xml" }, EMPTY_CLASS_ARRAY,
DelegatingSmartContextLoader.class);
}
@Test
public void buildMergedContextConfigurationWithLocalAnnotationAndClasses() {
public void buildMergedConfigWithLocalAnnotationAndClasses() {
Class<?> testClass = ClassesFoo.class;
MergedContextConfiguration mergedConfig = ContextLoaderUtils.buildMergedContextConfiguration(testClass, null);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
assertMergedContextConfiguration(mergedConfig, testClass, EMPTY_STRING_ARRAY,
new Class<?>[] { FooConfig.class }, DelegatingSmartContextLoader.class);
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, new Class<?>[] { FooConfig.class },
DelegatingSmartContextLoader.class);
}
@Test
public void buildMergedContextConfigurationWithLocalAnnotationAndOverriddenContextLoaderAndLocations() {
public void buildMergedConfigWithLocalAnnotationAndOverriddenContextLoaderAndLocations() {
Class<?> testClass = LocationsFoo.class;
Class<? extends ContextLoader> expectedContextLoaderClass = GenericPropertiesContextLoader.class;
MergedContextConfiguration mergedConfig = ContextLoaderUtils.buildMergedContextConfiguration(testClass,
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass,
expectedContextLoaderClass.getName());
assertMergedContextConfiguration(mergedConfig, testClass, new String[] { "classpath:/foo.xml" },
EMPTY_CLASS_ARRAY, expectedContextLoaderClass);
assertMergedConfig(mergedConfig, testClass, new String[] { "classpath:/foo.xml" }, EMPTY_CLASS_ARRAY,
expectedContextLoaderClass);
}
@Test
public void buildMergedContextConfigurationWithLocalAnnotationAndOverriddenContextLoaderAndClasses() {
public void buildMergedConfigWithLocalAnnotationAndOverriddenContextLoaderAndClasses() {
Class<?> testClass = ClassesFoo.class;
Class<? extends ContextLoader> expectedContextLoaderClass = GenericPropertiesContextLoader.class;
MergedContextConfiguration mergedConfig = ContextLoaderUtils.buildMergedContextConfiguration(testClass,
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass,
expectedContextLoaderClass.getName());
assertMergedContextConfiguration(mergedConfig, testClass, EMPTY_STRING_ARRAY,
new Class<?>[] { FooConfig.class }, expectedContextLoaderClass);
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, new Class<?>[] { FooConfig.class },
expectedContextLoaderClass);
}
@Test
public void buildMergedContextConfigurationWithLocalAndInheritedAnnotationsAndLocations() {
public void buildMergedConfigWithLocalAndInheritedAnnotationsAndLocations() {
Class<?> testClass = LocationsBar.class;
String[] expectedLocations = new String[] { "/foo.xml", "/bar.xml" };
MergedContextConfiguration mergedConfig = ContextLoaderUtils.buildMergedContextConfiguration(testClass, null);
assertMergedContextConfiguration(mergedConfig, testClass, expectedLocations, EMPTY_CLASS_ARRAY,
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
assertMergedConfig(mergedConfig, testClass, expectedLocations, EMPTY_CLASS_ARRAY,
AnnotationConfigContextLoader.class);
}
@Test
public void buildMergedContextConfigurationWithLocalAndInheritedAnnotationsAndClasses() {
public void buildMergedConfigWithLocalAndInheritedAnnotationsAndClasses() {
Class<?> testClass = ClassesBar.class;
Class<?>[] expectedClasses = new Class<?>[] { FooConfig.class, BarConfig.class };
MergedContextConfiguration mergedConfig = ContextLoaderUtils.buildMergedContextConfiguration(testClass, null);
assertMergedContextConfiguration(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses,
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses,
AnnotationConfigContextLoader.class);
}
@Test
public void buildMergedConfigWithAnnotationsAndOverriddenLocations() {
Class<?> testClass = OverriddenLocationsBar.class;
String[] expectedLocations = new String[] { "/bar.xml" };
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
assertMergedConfig(mergedConfig, testClass, expectedLocations, EMPTY_CLASS_ARRAY,
AnnotationConfigContextLoader.class);
}
@Test
public void buildMergedConfigWithAnnotationsAndOverriddenClasses() {
Class<?> testClass = OverriddenClassesBar.class;
Class<?>[] expectedClasses = new Class<?>[] { BarConfig.class };
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses,
AnnotationConfigContextLoader.class);
}
@Test
public void buildMergedConfigWithLocalInitializer() {
Class<?> testClass = InitializersFoo.class;
Class<?>[] expectedClasses = new Class<?>[] { FooConfig.class };
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> expectedInitializerClasses//
= new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
expectedInitializerClasses.add(FooInitializer.class);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
DelegatingSmartContextLoader.class);
}
@Test
public void buildMergedConfigWithLocalAndInheritedInitializer() {
Class<?> testClass = InitializersBar.class;
Class<?>[] expectedClasses = new Class<?>[] { FooConfig.class, BarConfig.class };
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> expectedInitializerClasses//
= new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
expectedInitializerClasses.add(FooInitializer.class);
expectedInitializerClasses.add(BarInitializer.class);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
DelegatingSmartContextLoader.class);
}
@Test
public void buildMergedConfigWithOverriddenInitializers() {
Class<?> testClass = OverriddenInitializersBar.class;
Class<?>[] expectedClasses = new Class<?>[] { FooConfig.class, BarConfig.class };
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> expectedInitializerClasses//
= new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
expectedInitializerClasses.add(BarInitializer.class);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
DelegatingSmartContextLoader.class);
}
@Test
public void buildMergedConfigWithOverriddenInitializersAndClasses() {
Class<?> testClass = OverriddenInitializersAndClassesBar.class;
Class<?>[] expectedClasses = new Class<?>[] { BarConfig.class };
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> expectedInitializerClasses//
= new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
expectedInitializerClasses.add(BarInitializer.class);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
DelegatingSmartContextLoader.class);
}
@Test
public void resolveActiveProfilesWithoutAnnotation() {
String[] profiles = ContextLoaderUtils.resolveActiveProfiles(Enigma.class);
String[] profiles = resolveActiveProfiles(Enigma.class);
assertArrayEquals(EMPTY_STRING_ARRAY, profiles);
}
@Test
public void resolveActiveProfilesWithNoProfilesDeclared() {
String[] profiles = ContextLoaderUtils.resolveActiveProfiles(BareAnnotations.class);
String[] profiles = resolveActiveProfiles(BareAnnotations.class);
assertArrayEquals(EMPTY_STRING_ARRAY, profiles);
}
@Test
public void resolveActiveProfilesWithEmptyProfiles() {
String[] profiles = ContextLoaderUtils.resolveActiveProfiles(EmptyProfiles.class);
String[] profiles = resolveActiveProfiles(EmptyProfiles.class);
assertArrayEquals(EMPTY_STRING_ARRAY, profiles);
}
@Test
public void resolveActiveProfilesWithDuplicatedProfiles() {
String[] profiles = ContextLoaderUtils.resolveActiveProfiles(DuplicatedProfiles.class);
String[] profiles = resolveActiveProfiles(DuplicatedProfiles.class);
assertNotNull(profiles);
assertEquals(3, profiles.length);
@ -242,28 +335,28 @@ public class ContextLoaderUtilsTests {
@Test
public void resolveActiveProfilesWithLocalAnnotation() {
String[] profiles = ContextLoaderUtils.resolveActiveProfiles(LocationsFoo.class);
String[] profiles = resolveActiveProfiles(LocationsFoo.class);
assertNotNull(profiles);
assertArrayEquals(new String[] { "foo" }, profiles);
}
@Test
public void resolveActiveProfilesWithInheritedAnnotationAndLocations() {
String[] profiles = ContextLoaderUtils.resolveActiveProfiles(InheritedLocationsFoo.class);
String[] profiles = resolveActiveProfiles(InheritedLocationsFoo.class);
assertNotNull(profiles);
assertArrayEquals(new String[] { "foo" }, profiles);
}
@Test
public void resolveActiveProfilesWithInheritedAnnotationAndClasses() {
String[] profiles = ContextLoaderUtils.resolveActiveProfiles(InheritedClassesFoo.class);
String[] profiles = resolveActiveProfiles(InheritedClassesFoo.class);
assertNotNull(profiles);
assertArrayEquals(new String[] { "foo" }, profiles);
}
@Test
public void resolveActiveProfilesWithLocalAndInheritedAnnotations() {
String[] profiles = ContextLoaderUtils.resolveActiveProfiles(LocationsBar.class);
String[] profiles = resolveActiveProfiles(LocationsBar.class);
assertNotNull(profiles);
assertEquals(2, profiles.length);
@ -274,7 +367,7 @@ public class ContextLoaderUtilsTests {
@Test
public void resolveActiveProfilesWithOverriddenAnnotation() {
String[] profiles = ContextLoaderUtils.resolveActiveProfiles(Animals.class);
String[] profiles = resolveActiveProfiles(Animals.class);
assertNotNull(profiles);
assertEquals(2, profiles.length);
@ -333,13 +426,51 @@ public class ContextLoaderUtilsTests {
private static class LocationsBar extends LocationsFoo {
}
@ContextConfiguration(locations = "/bar.xml", inheritLocations = false, loader = AnnotationConfigContextLoader.class)
@ActiveProfiles("bar")
private static class OverriddenLocationsBar extends LocationsFoo {
}
@ContextConfiguration(classes = BarConfig.class, inheritLocations = true, loader = AnnotationConfigContextLoader.class)
@ActiveProfiles("bar")
private static class ClassesBar extends ClassesFoo {
}
@ContextConfiguration(classes = BarConfig.class, inheritLocations = false, loader = AnnotationConfigContextLoader.class)
@ActiveProfiles("bar")
private static class OverriddenClassesBar extends ClassesFoo {
}
@ActiveProfiles(profiles = { "dog", "cat" }, inheritProfiles = false)
private static class Animals extends LocationsBar {
}
private static class FooInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
public void initialize(GenericApplicationContext applicationContext) {
}
}
private static class BarInitializer implements ApplicationContextInitializer<GenericWebApplicationContext> {
public void initialize(GenericWebApplicationContext applicationContext) {
}
}
@ContextConfiguration(classes = FooConfig.class, initializers = FooInitializer.class)
private static class InitializersFoo {
}
@ContextConfiguration(classes = BarConfig.class, initializers = BarInitializer.class)
private static class InitializersBar extends InitializersFoo {
}
@ContextConfiguration(classes = BarConfig.class, initializers = BarInitializer.class, inheritInitializers = false)
private static class OverriddenInitializersBar extends InitializersFoo {
}
@ContextConfiguration(classes = BarConfig.class, inheritLocations = false, initializers = BarInitializer.class, inheritInitializers = false)
private static class OverriddenInitializersAndClassesBar extends InitializersFoo {
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 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.
@ -16,17 +16,25 @@
package org.springframework.test.context;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.test.context.support.GenericXmlContextLoader;
/**
* Unit tests for {@link MergedContextConfiguration}.
*
* <p>These tests primarily exist to ensure that {@code MergedContextConfiguration}
* can safely be used as the cache key for {@link ContextCache}.
*
* @author Sam Brannen
* @since 3.1
*/
@ -42,6 +50,7 @@ public class MergedContextConfigurationTests {
public void hashCodeWithNulls() {
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(null, null, null, null, null);
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(null, null, null, null, null);
assertTrue(mergedConfig1.hashCode() > 0);
assertEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
}
@ -155,6 +164,42 @@ public class MergedContextConfigurationTests {
assertFalse(mergedConfig1.hashCode() == mergedConfig2.hashCode());
}
@Test
public void hashCodeWithSameInitializers() {
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses1 = //
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
initializerClasses1.add(FooInitializer.class);
initializerClasses1.add(BarInitializer.class);
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses2 = //
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
initializerClasses2.add(BarInitializer.class);
initializerClasses2.add(FooInitializer.class);
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, initializerClasses1, EMPTY_STRING_ARRAY, loader);
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, initializerClasses2, EMPTY_STRING_ARRAY, loader);
assertEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
}
@Test
public void hashCodeWithDifferentInitializers() {
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses1 = //
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
initializerClasses1.add(FooInitializer.class);
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses2 = //
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
initializerClasses2.add(BarInitializer.class);
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, initializerClasses1, EMPTY_STRING_ARRAY, loader);
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, initializerClasses2, EMPTY_STRING_ARRAY, loader);
assertFalse(mergedConfig1.hashCode() == mergedConfig2.hashCode());
}
@Test
public void equalsBasics() {
MergedContextConfiguration mergedConfig = new MergedContextConfiguration(null, null, null, null, null);
@ -193,6 +238,7 @@ public class MergedContextConfigurationTests {
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, new AnnotationConfigContextLoader());
assertFalse(mergedConfig1.equals(mergedConfig2));
assertFalse(mergedConfig2.equals(mergedConfig1));
}
@Test
@ -214,6 +260,7 @@ public class MergedContextConfigurationTests {
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), locations2,
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
assertFalse(mergedConfig1.equals(mergedConfig2));
assertFalse(mergedConfig2.equals(mergedConfig1));
}
@Test
@ -235,6 +282,7 @@ public class MergedContextConfigurationTests {
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
classes2, EMPTY_STRING_ARRAY, loader);
assertFalse(mergedConfig1.equals(mergedConfig2));
assertFalse(mergedConfig2.equals(mergedConfig1));
}
@Test
@ -278,6 +326,57 @@ public class MergedContextConfigurationTests {
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, activeProfiles2, loader);
assertFalse(mergedConfig1.equals(mergedConfig2));
assertFalse(mergedConfig2.equals(mergedConfig1));
}
@Test
public void equalsWithSameInitializers() {
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses1 = //
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
initializerClasses1.add(FooInitializer.class);
initializerClasses1.add(BarInitializer.class);
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses2 = //
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
initializerClasses2.add(BarInitializer.class);
initializerClasses2.add(FooInitializer.class);
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, initializerClasses1, EMPTY_STRING_ARRAY, loader);
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, initializerClasses2, EMPTY_STRING_ARRAY, loader);
assertEquals(mergedConfig1, mergedConfig2);
}
@Test
public void equalsWithDifferentInitializers() {
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses1 = //
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
initializerClasses1.add(FooInitializer.class);
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses2 = //
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
initializerClasses2.add(BarInitializer.class);
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, initializerClasses1, EMPTY_STRING_ARRAY, loader);
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, initializerClasses2, EMPTY_STRING_ARRAY, loader);
assertFalse(mergedConfig1.equals(mergedConfig2));
assertFalse(mergedConfig2.equals(mergedConfig1));
}
private static class FooInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
public void initialize(GenericApplicationContext applicationContext) {
}
}
private static class BarInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
public void initialize(GenericApplicationContext applicationContext) {
}
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2002-2012 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
*
* http://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.junit4.aci;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.test.context.junit4.aci.annotation.InitializerWithoutConfigFilesOrClassesTest;
import org.springframework.test.context.junit4.aci.annotation.MergedInitializersAnnotationConfigTests;
import org.springframework.test.context.junit4.aci.annotation.MultipleInitializersAnnotationConfigTests;
import org.springframework.test.context.junit4.aci.annotation.OrderedInitializersAnnotationConfigTests;
import org.springframework.test.context.junit4.aci.annotation.OverriddenInitializersAnnotationConfigTests;
import org.springframework.test.context.junit4.aci.annotation.SingleInitializerAnnotationConfigTests;
import org.springframework.test.context.junit4.aci.xml.MultipleInitializersXmlConfigTests;
/**
* Convenience test suite for integration tests that verify support for
* {@link ApplicationContextInitializer ApplicationContextInitializers} (ACIs)
* in the TestContext framework.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(Suite.class)
// Note: the following 'multi-line' layout is for enhanced code readability.
@SuiteClasses({//
MultipleInitializersXmlConfigTests.class,//
SingleInitializerAnnotationConfigTests.class,//
MultipleInitializersAnnotationConfigTests.class,//
MergedInitializersAnnotationConfigTests.class,//
OverriddenInitializersAnnotationConfigTests.class,//
OrderedInitializersAnnotationConfigTests.class,//
InitializerWithoutConfigFilesOrClassesTest.class //
})
public class AciTestSuite {
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2002-2012 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
*
* http://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.junit4.aci;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.support.GenericApplicationContext;
/**
* @author Sam Brannen
* @since 3.2
*/
public class DevProfileInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
public void initialize(GenericApplicationContext applicationContext) {
applicationContext.getEnvironment().setActiveProfiles("dev");
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2002-2012 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
*
* http://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.junit4.aci;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.support.GenericApplicationContext;
/**
* @author Sam Brannen
* @since 3.2
*/
public class FooBarAliasInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
public void initialize(GenericApplicationContext applicationContext) {
applicationContext.registerAlias("foo", "bar");
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2002-2012 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
*
* http://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.junit4.aci.annotation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
/**
* @author Sam Brannen
* @since 3.2
*/
@Configuration
@Profile("dev")
class DevProfileConfig {
@Bean
public String baz() {
return "dev profile config";
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2002-2012 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
*
* http://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.junit4.aci.annotation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Sam Brannen
* @since 3.2
*/
@Configuration
class GlobalConfig {
@Bean
public String foo() {
return "foo";
}
@Bean
public String baz() {
return "global config";
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2002-2012 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
*
* http://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.junit4.aci.annotation;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.aci.annotation.InitializerWithoutConfigFilesOrClassesTest.EntireAppInitializer;
/**
* Integration test that verifies support for {@link ApplicationContextInitializer
* ApplicationContextInitializers} in the TestContext framework when the test
* class declares neither XML configuration files nor annotated configuration classes.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(initializers = EntireAppInitializer.class)
public class InitializerWithoutConfigFilesOrClassesTest {
@Autowired
private String foo;
@Test
public void foo() {
assertEquals("foo", foo);
}
static class EntireAppInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
public void initialize(GenericApplicationContext applicationContext) {
new AnnotatedBeanDefinitionReader(applicationContext).register(GlobalConfig.class);
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2002-2012 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
*
* http://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.junit4.aci.annotation;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.aci.DevProfileInitializer;
/**
* Integration tests that verify support for {@link ApplicationContextInitializer
* ApplicationContextInitializers} in conjunction with annotation-driven
* configuration in the TestContext framework.
*
* @author Sam Brannen
* @since 3.2
*/
@ContextConfiguration(initializers = DevProfileInitializer.class)
public class MergedInitializersAnnotationConfigTests extends SingleInitializerAnnotationConfigTests {
@Test
public void activeBeans() {
assertEquals("foo", foo);
assertEquals("foo", bar);
assertEquals("dev profile config", baz);
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2002-2012 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
*
* http://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.junit4.aci.annotation;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.aci.DevProfileInitializer;
import org.springframework.test.context.junit4.aci.FooBarAliasInitializer;
/**
* Integration tests that verify support for {@link ApplicationContextInitializer
* ApplicationContextInitializers} in conjunction with annotation-driven
* configuration in the TestContext framework.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { GlobalConfig.class, DevProfileConfig.class }, initializers = {
FooBarAliasInitializer.class, DevProfileInitializer.class })
public class MultipleInitializersAnnotationConfigTests {
@Autowired
private String foo, bar, baz;
@Test
public void activeBeans() {
assertEquals("foo", foo);
assertEquals("foo", bar);
assertEquals("dev profile config", baz);
}
}

View File

@ -0,0 +1,145 @@
/*
* Copyright 2002-2012 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
*
* http://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.junit4.aci.annotation;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.aci.annotation.OrderedInitializersAnnotationConfigTests.ConfigTwo;
import org.springframework.test.context.junit4.aci.annotation.OrderedInitializersAnnotationConfigTests.ConfigOne;
import org.springframework.test.context.junit4.aci.annotation.OrderedInitializersAnnotationConfigTests.GlobalConfig;
import org.springframework.test.context.junit4.aci.annotation.OrderedInitializersAnnotationConfigTests.OrderedOneInitializer;
import org.springframework.test.context.junit4.aci.annotation.OrderedInitializersAnnotationConfigTests.OrderedTwoInitializer;
/**
* Integration tests that verify that any {@link ApplicationContextInitializer
* ApplicationContextInitializers} implementing
* {@link org.springframework.core.Ordered Ordered} or marked with
* {@link org.springframework.core.annotation.Order @Order} will be sorted
* appropriately in conjunction with annotation-driven configuration in the
* TestContext framework.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
// Note: the ordering of the config classes is intentionally: global, two, one.
// Note: the ordering of the initializers is intentionally: two, one.
@ContextConfiguration(classes = { GlobalConfig.class, ConfigTwo.class, ConfigOne.class }, initializers = {
OrderedTwoInitializer.class, OrderedOneInitializer.class })
public class OrderedInitializersAnnotationConfigTests {
private static final String PROFILE_GLOBAL = "global";
private static final String PROFILE_ONE = "one";
private static final String PROFILE_TWO = "two";
@Autowired
private String foo, bar, baz;
@Test
public void activeBeans() {
assertEquals(PROFILE_GLOBAL, foo);
assertEquals(PROFILE_GLOBAL, bar);
assertEquals(PROFILE_TWO, baz);
}
// -------------------------------------------------------------------------
@Configuration
static class GlobalConfig {
@Bean
public String foo() {
return PROFILE_GLOBAL;
}
@Bean
public String bar() {
return PROFILE_GLOBAL;
}
@Bean
public String baz() {
return PROFILE_GLOBAL;
}
}
@Configuration
@Profile(PROFILE_ONE)
static class ConfigOne {
@Bean
public String foo() {
return PROFILE_ONE;
}
@Bean
public String bar() {
return PROFILE_ONE;
}
@Bean
public String baz() {
return PROFILE_ONE;
}
}
@Configuration
@Profile(PROFILE_TWO)
static class ConfigTwo {
@Bean
public String baz() {
return PROFILE_TWO;
}
}
// -------------------------------------------------------------------------
static class OrderedOneInitializer implements ApplicationContextInitializer<GenericApplicationContext>, Ordered {
public void initialize(GenericApplicationContext applicationContext) {
applicationContext.getEnvironment().setActiveProfiles(PROFILE_ONE);
}
public int getOrder() {
return 1;
}
}
@Order(2)
static class OrderedTwoInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
public void initialize(GenericApplicationContext applicationContext) {
applicationContext.getEnvironment().setActiveProfiles(PROFILE_TWO);
}
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2002-2012 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
*
* http://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.junit4.aci.annotation;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.aci.DevProfileInitializer;
/**
* Integration tests that verify support for {@link ApplicationContextInitializer
* ApplicationContextInitializers} in conjunction with annotation-driven
* configuration in the TestContext framework.
*
* @author Sam Brannen
* @since 3.2
*/
@ContextConfiguration(initializers = DevProfileInitializer.class, inheritInitializers = false)
public class OverriddenInitializersAnnotationConfigTests extends SingleInitializerAnnotationConfigTests {
@Test
@Override
public void activeBeans() {
assertEquals("foo", foo);
assertNull(bar);
assertEquals("dev profile config", baz);
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright 2002-2012 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
*
* http://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.junit4.aci.annotation;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.aci.FooBarAliasInitializer;
/**
* Integration tests that verify support for {@link ApplicationContextInitializer
* ApplicationContextInitializers} in conjunction with annotation-driven
* configuration in the TestContext framework.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { GlobalConfig.class, DevProfileConfig.class }, initializers = FooBarAliasInitializer.class)
public class SingleInitializerAnnotationConfigTests {
@Autowired
protected String foo;
@Autowired(required = false)
@Qualifier("bar")
protected String bar;
@Autowired
protected String baz;
@Test
public void activeBeans() {
assertEquals("foo", foo);
assertEquals("foo", bar);
assertEquals("global config", baz);
}
}

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="foo" class="java.lang.String">
<constructor-arg value="foo" />
</bean>
<bean id="baz" class="java.lang.String">
<constructor-arg value="global config" />
</bean>
<beans profile="dev">
<bean id="baz" class="java.lang.String">
<constructor-arg value="dev profile config" />
</bean>
</beans>
</beans>

View File

@ -0,0 +1,53 @@
/*
* Copyright 2002-2012 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
*
* http://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.junit4.aci.xml;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.aci.DevProfileInitializer;
import org.springframework.test.context.junit4.aci.FooBarAliasInitializer;
/**
* Integration tests that verify support for {@link ApplicationContextInitializer
* ApplicationContextInitializers} in conjunction with XML configuration files
* in the TestContext framework.
*
* @author Sam Brannen
* @since 3.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(initializers = { FooBarAliasInitializer.class, DevProfileInitializer.class })
public class MultipleInitializersXmlConfigTests {
@Autowired
private String foo, bar, baz;
@Test
public void activeBeans() {
assertEquals("foo", foo);
assertEquals("foo", bar);
assertEquals("dev profile config", baz);
}
}

View File

@ -59,8 +59,8 @@
linkend="new-in-3.1-property-source-abstraction" />).
<classname>MockEnvironment</classname> and
<classname>MockPropertySource</classname> are useful for developing
<emphasis>out-of-container</emphasis> unit tests for code that depends
on environment-specific properties.</para>
<emphasis>out-of-container</emphasis> tests for code that depends on
environment-specific properties.</para>
</section>
<section xml:id="mock-objects-jndi">
@ -271,20 +271,20 @@
container take time to instantiate. For example, a project with 50 to
100 Hibernate mapping files might take 10 to 20 seconds to load the
mapping files, and incurring that cost before running every test in
every test fixture leads to slower overall test runs that could reduce
productivity.</para>
every test fixture leads to slower overall test runs that reduce
developer productivity.</para>
<para>Test classes can provide either an array of <emphasis>resource
locations</emphasis> for XML configuration metadata — typically in the
classpath — or an array of <emphasis>annotated classes</emphasis> that
is used to configure the application. These locations or classes are
the same as or similar to those specified in
<para>Test classes typically declare either an array of
<emphasis>resource locations</emphasis> for XML configuration metadata
— often in the classpath — or an array of <emphasis>annotated
classes</emphasis> that is used to configure the application. These
locations or classes are the same as or similar to those specified in
<literal>web.xml</literal> or other deployment configuration
files.</para>
<para>By default, once loaded, the configured
<interfacename>ApplicationContext</interfacename> is reused for each
test. Thus the setup cost is incurred only once (per test suite), and
test. Thus the setup cost is incurred only once per test suite, and
subsequent test execution is much faster. In this context, the term
<emphasis>test suite</emphasis> means all tests run in the same JVM —
for example, all tests run from an Ant, Maven, or Gradle build for a
@ -311,11 +311,10 @@
contexts across various testing scenarios (e.g., for configuring
Spring-managed object graphs, transactional proxies,
<classname>DataSource</classname>s, etc.), thus avoiding the need to
duplicate complex test fixture set up for individual test
cases.</para>
duplicate complex test fixture setup for individual test cases.</para>
<para>As an example, consider the scenario where we have a class,
<classname>HibernateTitleRepository</classname>, that performs data
<classname>HibernateTitleRepository</classname>, that implements data
access logic for a <classname>Title</classname> domain entity. We want
to write integration tests that test the following areas:</para>
@ -348,7 +347,7 @@
<title>Transaction management</title>
<para>One common issue in tests that access a real database is their
affect on the state of the persistence store. Even when you're using a
effect on the state of the persistence store. Even when you're using a
development database, changes to the state may affect future tests.
Also, many operations — such as inserting or modifying persistent data
— cannot be performed (or verified) outside a transaction.</para>
@ -358,11 +357,11 @@
simply write code that can assume the existence of a transaction. If
you call transactionally proxied objects in your tests, they will
behave correctly, according to their configured transactional
semantics. In addition, if test methods delete the contents of
selected tables while running within a transaction, the transaction
will roll back by default, and the database will return to its state
prior to execution of the test. Transactional support is provided to
your test class via a
semantics. In addition, if a test method deletes the contents of
selected tables while running within the transaction managed for the
test, the transaction will roll back by default, and the database will
return to its state prior to execution of the test. Transactional
support is provided to a test via a
<classname>PlatformTransactionManager</classname> bean defined in the
test's application context.</para>
@ -370,10 +369,10 @@
useful when you want a particular test to populate or modify the
database — the TestContext framework can be instructed to cause the
transaction to commit instead of roll back via the <link
linkend="integration-testing-annotations">
<interfacename>@TransactionConfiguration</interfacename> </link> and
<link linkend="integration-testing-annotations">
<interfacename>@Rollback</interfacename> </link> annotations.</para>
linkend="integration-testing-annotations"><interfacename>@TransactionConfiguration</interfacename></link>
and <link
linkend="integration-testing-annotations"><interfacename>@Rollback</interfacename></link>
annotations.</para>
<para>See transaction management with the <link
linkend="testcontext-tx">TestContext framework</link>.</para>
@ -396,8 +395,8 @@
</listitem>
<listitem>
<para>A <classname>JdbcTemplate</classname>, for executing
SQL statements to query the database. Such queries can be used to
<para>A <classname>JdbcTemplate</classname>, for executing SQL
statements to query the database. Such queries can be used to
confirm database state both <emphasis>prior to</emphasis> and
<emphasis>after</emphasis> execution of database-related
application code, and Spring ensures that such queries run in the
@ -422,14 +421,13 @@
<title>JDBC Testing Support</title>
<para>The <literal>org.springframework.test.jdbc</literal> package
contains <classname>JdbcTestUtils</classname>, which is a
collection of JDBC related utility functions intended to simplify
standard database testing scenarios. <emphasis>Note that <link
linkend="testcontext-support-classes-junit4">
<classname>AbstractTransactionalJUnit4SpringContextTests</classname>
</link> and <link linkend="testcontext-support-classes-testng">
<classname>AbstractTransactionalTestNGSpringContextTests</classname>
</link> provide convenience methods which delegate to
contains <classname>JdbcTestUtils</classname>, which is a collection of
JDBC related utility functions intended to simplify standard database
testing scenarios. <emphasis>Note that <link
linkend="testcontext-support-classes-junit4"><classname>AbstractTransactionalJUnit4SpringContextTests</classname></link>
and <link
linkend="testcontext-support-classes-testng"><classname>AbstractTransactionalTestNGSpringContextTests</classname></link>
provide convenience methods which delegate to
<classname>JdbcTestUtils</classname> internally.</emphasis></para>
<para>The <literal>spring-jdbc</literal> module provides support for
@ -460,8 +458,8 @@
<para>Defines class-level metadata that is used to determine how
to load and configure an
<interfacename>ApplicationContext</interfacename> for test
classes. Specifically,
<interfacename>ApplicationContext</interfacename> for integration
tests. Specifically,
<interfacename>@ContextConfiguration</interfacename> declares
<emphasis>either</emphasis> the application context resource
<varname>locations</varname> <emphasis>or</emphasis> the annotated
@ -471,8 +469,8 @@
<para>Resource locations are typically XML configuration files
located in the classpath; whereas, annotated classes are typically
<interfacename>@Configuration</interfacename> classes. However,
resource locations could also refer to files in the file system,
and annotated classes could be component classes, etc.</para>
resource locations can also refer to files in the file system, and
annotated classes can be component classes, etc.</para>
<programlisting language="java"><emphasis role="bold">@ContextConfiguration</emphasis>("/test-config.xml")
public class XmlApplicationContextTests {
@ -485,13 +483,27 @@ public class ConfigClassApplicationContextTests {
<lineannotation>// class body...</lineannotation>
}</programlisting>
<para>As an alternative or in addition to declaring resource
locations or annotated classes,
<interfacename>@ContextConfiguration</interfacename> may be used
to declare
<interfacename>ApplicationContextInitializer</interfacename>
classes.</para>
<programlisting language="java"><emphasis role="bold">@ContextConfiguration</emphasis>(<emphasis
role="bold">initializers</emphasis>=CustomContextIntializer.class)
public class ContextInitializerTests {
<lineannotation>// class body...</lineannotation>
}</programlisting>
<para><interfacename>@ContextConfiguration</interfacename> may
optionally be used to declare the
<interfacename>ContextLoader</interfacename> strategy as well.
Note, however, that you typically do not need to explicitly
configure the loader since the default loader supports either
resource <varname>locations</varname> or annotated
<varname>classes</varname>.</para>
<varname>classes</varname> as well as
<varname>initializers</varname>.</para>
<programlisting language="java"><emphasis role="bold">@ContextConfiguration</emphasis>(<emphasis
role="bold">locations</emphasis>="/test-context.xml", <emphasis
@ -503,8 +515,8 @@ public class CustomLoaderXmlApplicationContextTests {
<note>
<para><interfacename>@ContextConfiguration</interfacename>
provides support for <emphasis>inheriting</emphasis> resource
locations or configuration classes declared by superclasses by
default.</para>
locations or configuration classes as well as context
initializers declared by superclasses by default.</para>
</note>
<para>See <link linkend="testcontext-ctx-management">Context
@ -650,8 +662,8 @@ public class CustomTestExecutionListenerTests {
should be used to drive transactions can be explicitly specified
if there are multiple beans of type
<interfacename>PlatformTransactionManager</interfacename> in the
test's <interfacename>ApplicationContext</interfacename> and the
bean name of the desired
test's <interfacename>ApplicationContext</interfacename> and if
the bean name of the desired
<interfacename>PlatformTransactionManager</interfacename> is not
"transactionManager". In addition, you can change the
<literal>defaultRollback</literal> flag to
@ -1227,15 +1239,21 @@ public class MyTest {
application context resource <literal>locations</literal> or annotated
<varname>classes</varname>, the configured
<interfacename>ContextLoader</interfacename> determines how to load a
context from a default location or default configuration
classes.</para>
context from a default location or default configuration classes. In
addition to context resource <varname>locations</varname> and
annotated <varname>classes</varname>, an application context can also
be configured via application context
<varname>initializers</varname>.</para>
<para>The following sections explain how to configure an
<interfacename>ApplicationContext</interfacename> via XML
configuration files or annotated classes (typically
<interfacename>@Configuration</interfacename> classes) using Spring's
<interfacename>@ContextConfiguration</interfacename>
annotation.</para>
configuration files, annotated classes (typically
<interfacename>@Configuration</interfacename> classes), or context
initializers using Spring's
<interfacename>@ContextConfiguration</interfacename> annotation.
Alternatively, you can implement and configure your own custom
<interfacename>SmartContextLoader</interfacename> for advanced use
cases.</para>
<section xml:id="testcontext-ctx-management-xml">
<title>Context configuration with XML resources</title>
@ -1245,18 +1263,16 @@ public class MyTest {
class with <interfacename>@ContextConfiguration</interfacename> and
configure the <literal>locations</literal> attribute with an array
that contains the resource locations of XML configuration metadata.
A plain path — for example <literal>"context.xml"</literal> — will
be treated as a classpath resource that is relative to the package
in which the test class is defined. A path starting with a slash is
treated as an absolute classpath location, for example
A plain or relative path — for example
<literal>"context.xml"</literal> — will be treated as a classpath
resource that is relative to the package in which the test class is
defined. A path starting with a slash is treated as an absolute
classpath location, for example
<literal>"/org/example/config.xml"</literal>. A path which
represents a resource URL (i.e., a path prefixed with
<literal>classpath:</literal>, <literal>file:</literal>,
<literal>http:</literal>, etc.) will be used <emphasis>as
is</emphasis>. Alternatively, you can implement and configure your
own custom <interfacename>ContextLoader</interfacename> or
<interfacename>SmartContextLoader</interfacename> for advanced use
cases.</para>
is</emphasis>.</para>
<programlisting language="java">@RunWith(SpringJUnit4ClassRunner.class)
<lineannotation>// ApplicationContext will be loaded from "/app-config.xml" and
@ -1269,10 +1285,10 @@ public class MyTest {
<para><interfacename>@ContextConfiguration</interfacename> supports
an alias for the <literal>locations</literal> attribute through the
standard Java <literal>value</literal> attribute. Thus, if you do
not need to configure a custom
<interfacename>ContextLoader</interfacename>, you can omit the
declaration of the <literal>locations</literal> attribute name and
declare the resource locations by using the shorthand format
not need to declare additional attributes in
<interfacename>@ContextConfiguration</interfacename>, you can omit
the declaration of the <literal>locations</literal> attribute name
and declare the resource locations by using the shorthand format
demonstrated in the following example.</para>
<programlisting language="java">@RunWith(SpringJUnit4ClassRunner.class)
@ -1296,7 +1312,8 @@ public class MyTest {
<programlisting language="java">package com.example;
@RunWith(SpringJUnit4ClassRunner.class)
<lineannotation>// ApplicationContext will be loaded from "classpath:/com/example/MyTest-context.xml"</lineannotation>
<lineannotation>// ApplicationContext will be loaded from
// "classpath:/com/example/MyTest-context.xml"</lineannotation>
<emphasis role="bold">@ContextConfiguration</emphasis>
public class MyTest {
<lineannotation>// class body...</lineannotation>
@ -1311,15 +1328,11 @@ public class MyTest {
<xref linkend="beans-java" />), annotate your test class with
<interfacename>@ContextConfiguration</interfacename> and configure
the <literal>classes</literal> attribute with an array that contains
references to annotated classes. Alternatively, you can implement
and configure your own custom
<interfacename>ContextLoader</interfacename> or
<interfacename>SmartContextLoader</interfacename> for advanced use
cases.</para>
references to annotated classes.</para>
<programlisting language="java">@RunWith(SpringJUnit4ClassRunner.class)
<lineannotation>// ApplicationContext will be loaded from AppConfig and TestConfig</lineannotation>
<emphasis role="bold">@ContextConfiguration(classes={AppConfig.class, TestConfig.class})</emphasis>
<emphasis role="bold">@ContextConfiguration(classes = {AppConfig.class, TestConfig.class})</emphasis>
public class MyTest {
<lineannotation>// class body...</lineannotation>
}</programlisting>
@ -1340,10 +1353,9 @@ public class MyTest {
arbitrary. In addition, a test class can contain more than one
static inner configuration class if desired.</para>
<programlisting language="java">package com.example;
@RunWith(SpringJUnit4ClassRunner.class)
<lineannotation>// ApplicationContext will be loaded from the static inner Config class</lineannotation>
<programlisting language="java">@RunWith(SpringJUnit4ClassRunner.class)
<lineannotation>// ApplicationContext will be loaded from the
// static inner Config class</lineannotation>
<emphasis role="bold">@ContextConfiguration</emphasis>
public class OrderServiceTest {
@ -1406,27 +1418,85 @@ public class OrderServiceTest {
other type of configuration.</para>
</section>
<section xml:id="testcontext-ctx-management-initializers">
<title>Context configuration with context initializers</title>
<para>To configure an
<interfacename>ApplicationContext</interfacename> for your tests
using context initializers, annotate your test class with
<interfacename>@ContextConfiguration</interfacename> and configure
the <literal>initializers</literal> attribute with an array that
contains references to classes that implement
<interfacename>ApplicationContextInitializer</interfacename>. The
declared context initializers will then be used to initialize the
<interfacename>ConfigurableApplicationContext</interfacename> that
is loaded for your tests. Note that the concrete
<interfacename>ConfigurableApplicationContext</interfacename> type
supported by each declared initializer must be compatible with the
type of <interfacename>ApplicationContext</interfacename> created by
the <interfacename>SmartContextLoader</interfacename> in use (i.e.,
typically a <classname>GenericApplicationContext</classname>).
Furthermore, the order in which the initializers are invoked depends
on whether they implement Spring's
<interfacename>Ordered</interfacename> interface or are annotated
with Spring's <interfacename>@Order</interfacename>
annotation.</para>
<programlisting language="java">@RunWith(SpringJUnit4ClassRunner.class)
<lineannotation>// ApplicationContext will be loaded from TestConfig
</lineannotation><lineannotation>// and initialized by TestAppCtxInitializer</lineannotation>
<emphasis role="bold">@ContextConfiguration(
classes = TestConfig.class,
initializers = TestAppCtxInitializer.class)</emphasis>
public class MyTest {
<lineannotation>// class body...</lineannotation>
}</programlisting>
<para>It is also possible to omit the declaration of XML
configuration files or annotated classes in
<interfacename>@ContextConfiguration</interfacename> entirely and
instead declare only
<interfacename>ApplicationContextInitializer</interfacename> classes
which are then responsible for registering beans in the context —
for example, by programmatically loading bean definitions from XML
files or configuration classes.</para>
<programlisting language="java">@RunWith(SpringJUnit4ClassRunner.class)
<lineannotation>// ApplicationContext will be initialized by EntireAppInitializer
</lineannotation><lineannotation>// which presumably registers beans in the context</lineannotation>
<emphasis role="bold">@ContextConfiguration(initializers = EntireAppInitializer.class)</emphasis>
public class MyTest {
<lineannotation>// class body...</lineannotation>
}</programlisting>
</section>
<section xml:id="testcontext-ctx-management-inheritance">
<title>Context configuration inheritance</title>
<para><interfacename>@ContextConfiguration</interfacename> supports
a boolean <literal>inheritLocations</literal> attribute that denotes
whether resource locations or annotated classes declared by
superclasses should be <emphasis>inherited</emphasis>. The default
value is <literal>true</literal>. This means that a test class
inherits the resource locations or annotated classes declared by any
superclasses. Specifically, the resource locations or annotated
classes for a test class are appended to the list of resource
locations or annotated classes declared by superclasses. Thus,
subclasses have the option of <emphasis>extending</emphasis> the
list of resource locations or annotated classes.</para>
boolean <varname>inheritLocations</varname> and
<varname>inheritInitializers</varname> attributes that denote
whether resource locations or annotated classes and context
initializers declared by superclasses should be
<emphasis>inherited</emphasis>. The default value for both flags is
<literal>true</literal>. This means that a test class inherits the
resource locations or annotated classes as well as the context
initializers declared by any superclasses. Specifically, the
resource locations or annotated classes for a test class are
appended to the list of resource locations or annotated classes
declared by superclasses. Similarly, the initializers for a given
test class will be added to the set of initializers defined by test
superclasses. Thus, subclasses have the option of
<emphasis>extending</emphasis> the resource locations, annotated
classes, or context initializers.</para>
<para>If <interfacename>@ContextConfiguration</interfacename>'s
<literal>inheritLocations</literal> attribute is set to
<literal>inheritLocations</literal> or
<varname>inheritInitializers</varname> attribute is set to
<literal>false</literal>, the resource locations or annotated
classes for the test class <emphasis>shadow</emphasis> and
effectively replace any resource locations or annotated classes
defined by superclasses.</para>
classes and the context initializers, respectively, for the test
class <emphasis>shadow</emphasis> and effectively replace the
configuration defined by superclasses.</para>
<para>In the following example that uses XML resource locations, the
<interfacename>ApplicationContext</interfacename> for
@ -1439,14 +1509,15 @@ public class OrderServiceTest {
<emphasis>"base-config.xml"</emphasis>.</para>
<programlisting language="java">@RunWith(SpringJUnit4ClassRunner.class)
<lineannotation>// ApplicationContext will be loaded from "/base-config.xml" in the root of the classpath</lineannotation>
<lineannotation>// ApplicationContext will be loaded from "/base-config.xml"
// in the root of the classpath</lineannotation>
<emphasis role="bold">@ContextConfiguration("/base-config.xml")</emphasis>
public class BaseTest {
<lineannotation>// class body...</lineannotation>
}
<lineannotation>// ApplicationContext will be loaded from "/base-config.xml" and "/extended-config.xml"</lineannotation>
<lineannotation>// in the root of the classpath</lineannotation>
<lineannotation>// ApplicationContext will be loaded from "/base-config.xml" and
// "/extended-config.xml" </lineannotation><lineannotation>in the root of the classpath</lineannotation>
<emphasis role="bold">@ContextConfiguration("/extended-config.xml")</emphasis>
public class ExtendedTest extends BaseTest {
<lineannotation>// class body...</lineannotation>
@ -1463,13 +1534,38 @@ public class ExtendedTest extends BaseTest {
<programlisting language="java">@RunWith(SpringJUnit4ClassRunner.class)
<lineannotation>// ApplicationContext will be loaded from BaseConfig</lineannotation>
<emphasis role="bold">@ContextConfiguration(classes=BaseConfig.class)</emphasis>
<emphasis role="bold">@ContextConfiguration(classes = BaseConfig.class)</emphasis>
public class BaseTest {
<lineannotation>// class body...</lineannotation>
}
<lineannotation>// ApplicationContext will be loaded from BaseConfig and ExtendedConfig</lineannotation>
<emphasis role="bold">@ContextConfiguration(classes=ExtendedConfig.class)</emphasis>
<emphasis role="bold">@ContextConfiguration(classes = ExtendedConfig.class)</emphasis>
public class ExtendedTest extends BaseTest {
<lineannotation>// class body...</lineannotation>
}</programlisting>
<para>In the following example that uses context initializers, the
<interfacename>ApplicationContext</interfacename> for
<classname>ExtendedTest</classname> will be initialized using
<classname>BaseInitializer</classname> <emphasis
role="bold">and</emphasis>
<classname>ExtendedInitializer</classname>. Note, however, that the
order in which the initializers are invoked depends on whether they
implement Spring's <interfacename>Ordered</interfacename> interface
or are annotated with Spring's <interfacename>@Order</interfacename>
annotation.</para>
<programlisting language="java">@RunWith(SpringJUnit4ClassRunner.class)
<lineannotation>// ApplicationContext will be initialized by BaseInitializer</lineannotation>
<emphasis role="bold">@ContextConfiguration(initializers=BaseInitializer.class)</emphasis>
public class BaseTest {
<lineannotation>// class body...</lineannotation>
}
<lineannotation>// ApplicationContext will be initialized by BaseInitializer
// and ExtendedInitializer</lineannotation>
<emphasis role="bold">@ContextConfiguration(initializers=ExtendedInitializer.class)</emphasis>
public class ExtendedTest extends BaseTest {
<lineannotation>// class body...</lineannotation>
}</programlisting>
@ -1478,9 +1574,9 @@ public class ExtendedTest extends BaseTest {
<section xml:id="testcontext-ctx-management-env-profiles">
<title>Context configuration with environment profiles</title>
<para>Spring 3.1 introduces first-class support in the framework for
<para>Spring 3.1 introduced first-class support in the framework for
the notion of environments and profiles (a.k.a., <emphasis>bean
definition profiles</emphasis>), and integration tests can now be
definition profiles</emphasis>), and integration tests can be
configured to activate particular bean definition profiles for
various testing scenarios. This is achieved by annotating a test
class with the <interfacename>@ActiveProfiles</interfacename>
@ -1634,7 +1730,7 @@ public class TransferServiceConfig {
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
classes={
classes = {
TransferServiceConfig.class,
StandaloneDataConfig.class,
JndiDataConfig.class})
@ -1715,6 +1811,11 @@ public class TransferServiceTest {
@ContextConfiguration)</emphasis></para>
</listitem>
<listitem>
<para><varname>contextInitializerClasses</varname>
<emphasis>(from @ContextConfiguration)</emphasis></para>
</listitem>
<listitem>
<para><varname>contextLoader</varname> <emphasis>(from
@ContextConfiguration)</emphasis></para>
@ -1737,8 +1838,8 @@ public class TransferServiceTest {
also defines <literal>{"app-config.xml",
"test-config.xml"}</literal> for its locations (either explicitly or
implicitly through inheritance) and does not define a different
<interfacename>ContextLoader</interfacename> or different active
profiles, then the same
<interfacename>ContextLoader</interfacename>, different active
profiles, or different context initializers, then the same
<interfacename>ApplicationContext</interfacename> will be shared by
both test classes. This means that the setup cost for loading an
application context is incurred only once (per test suite), and
@ -2103,7 +2204,7 @@ public void updateWithSessionFlush() {
</section>
<section xml:id="testcontext-support-classes">
<title>TestContext support classes</title>
<title>TestContext Framework support classes</title>
<section xml:id="testcontext-support-classes-junit4">
<title>JUnit support classes</title>
@ -2157,14 +2258,14 @@ public void updateWithSessionFlush() {
</listitem>
<listitem>
<para><literal>jdbcTemplate</literal>: Use this
variable to execute SQL statements to query the database.
Such queries can be used to confirm database state both
<emphasis>prior to</emphasis> and <emphasis>after</emphasis>
execution of database-related application code, and Spring
ensures that such queries run in the scope of the same
transaction as the application code. When used in
conjunction with an ORM tool, be sure to avoid <link
<para><literal>jdbcTemplate</literal>: Use this variable to
execute SQL statements to query the database. Such queries
can be used to confirm database state both <emphasis>prior
to</emphasis> and <emphasis>after</emphasis> execution of
database-related application code, and Spring ensures that
such queries run in the scope of the same transaction as the
application code. When used in conjunction with an ORM tool,
be sure to avoid <link
linkend="testcontext-tx-false-positives">false
positives</link>.</para>
</listitem>
@ -2266,14 +2367,14 @@ public class SimpleTest {
</listitem>
<listitem>
<para><literal>jdbcTemplate</literal>: Use this
variable to execute SQL statements to query the database.
Such queries can be used to confirm database state both
<emphasis>prior to</emphasis> and <emphasis>after</emphasis>
execution of database-related application code, and Spring
ensures that such queries run in the scope of the same
transaction as the application code. When used in
conjunction with an ORM tool, be sure to avoid <link
<para><literal>jdbcTemplate</literal>: Use this variable to
execute SQL statements to query the database. Such queries
can be used to confirm database state both <emphasis>prior
to</emphasis> and <emphasis>after</emphasis> execution of
database-related application code, and Spring ensures that
such queries run in the scope of the same transaction as the
application code. When used in conjunction with an ORM tool,
be sure to avoid <link
linkend="testcontext-tx-false-positives">false
positives</link>.</para>
</listitem>