Merge branch '3.2.x'

* 3.2.x: (28 commits)
  Hide 'doc' changes from jdiff reports
  Document @Bean 'lite' mode vs @Configuration
  Final preparations for 3.2.2
  Remove Tiles 3 configuration method
  Polishing
  Extracted buildRequestAttributes template method from FrameworkServlet
  Added "beforeExistingAdvisors" flag to AbstractAdvisingBeanPostProcessor
  Minor refinements along the way of researching static CGLIB callbacks
  Compare Kind references before checking log levels
  Polish Javadoc in RequestAttributes
  Fix copy-n-paste errors in NativeWebRequest
  Fix issue with restoring included attributes
  Add additional test for daylight savings glitch
  Document context hierarchy support in the TCF
  Fix test for daylight savings glitch
  Make the methodParameter field of HandlerMethod final
  Disable AsyncTests in spring-test-mvc
  Reformat the testing chapter
  Document context hierarchy support in the TCF
  Document context hierarchy support in the TCF
  ...
This commit is contained in:
Phillip Webb
2013-03-13 14:01:46 -07:00
267 changed files with 8384 additions and 6246 deletions

View File

@@ -40,9 +40,9 @@ import org.springframework.util.ClassUtils;
*
* <p>There are various choices for DataSource implementations:
* <ul>
* <li>SingleConnectionDataSource (using the same Connection for all getConnection calls);
* <li>DriverManagerDataSource (creating a new Connection on each getConnection call);
* <li>Apache's Jakarta Commons DBCP offers BasicDataSource (a real pool).
* <li>{@code SingleConnectionDataSource} (using the same Connection for all getConnection calls)
* <li>{@code DriverManagerDataSource} (creating a new Connection on each getConnection call)
* <li>Apache's Jakarta Commons DBCP offers {@code org.apache.commons.dbcp.BasicDataSource} (a real pool)
* </ul>
*
* <p>Typical usage in bootstrap code:
@@ -77,7 +77,6 @@ import org.springframework.util.ClassUtils;
* @see SimpleNamingContext
* @see org.springframework.jdbc.datasource.SingleConnectionDataSource
* @see org.springframework.jdbc.datasource.DriverManagerDataSource
* @see org.apache.commons.dbcp.BasicDataSource
*/
public class SimpleNamingContextBuilder implements InitialContextFactoryBuilder {

View File

@@ -163,6 +163,7 @@ public class MockServletContext implements ServletContext {
* @param resourceLoader the ResourceLoader to use (or null for the default)
* @see #registerNamedDispatcher
*/
@SuppressWarnings("javadoc")
public MockServletContext(String resourceBasePath, ResourceLoader resourceLoader) {
this.resourceLoader = (resourceLoader != null ? resourceLoader : new DefaultResourceLoader());
this.resourceBasePath = (resourceBasePath != null ? resourceBasePath : "");
@@ -241,8 +242,16 @@ public class MockServletContext implements ServletContext {
return this.effectiveMinorVersion;
}
/**
* This method uses the Java Activation framework, which returns
* "application/octet-stream" when the mime type is unknown (i.e. it never returns
* {@code null}). In order to maintain the {@link ServletContext#getMimeType(String)
* contract, as of version 3.2.2, this method returns null if the mimeType is
* "application/octet-stream".
*/
public String getMimeType(String filePath) {
return MimeTypeResolver.getMimeType(filePath);
String mimeType = MimeTypeResolver.getMimeType(filePath);
return ("application/octet-stream".equals(mimeType)) ? null : mimeType;
}
public Set<String> getResourcePaths(String path) {
@@ -344,6 +353,7 @@ public class MockServletContext implements ServletContext {
* <p>Defaults to {@linkplain #COMMON_DEFAULT_SERVLET_NAME "default"}.
* @see #setDefaultServletName
*/
@SuppressWarnings("javadoc")
public String getDefaultServletName() {
return this.defaultServletName;
}

View File

@@ -197,6 +197,7 @@ public abstract class AbstractDependencyInjectionSpringContextTests extends Abst
* test instance has not been configured
* @see #populateProtectedVariables()
*/
@SuppressWarnings("javadoc")
protected void injectDependencies() throws Exception {
Assert.state(getApplicationContext() != null,
"injectDependencies() called without first configuring an ApplicationContext");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,13 +36,13 @@ import java.lang.annotation.Target;
* mode set to {@link ClassMode#AFTER_CLASS AFTER_CLASS}</li>
* </ul>
* <p>
* Use this annotation if a test has modified the context (for example, by
* replacing a bean definition). Subsequent tests will be supplied a new
* context.
* Use this annotation if a test has modified the context &mdash; for example, by
* replacing a bean definition or changing the state of a singleton bean.
* Subsequent tests will be supplied a new context.
* </p>
* <p>
* {@code &#064;DirtiesContext} may be used as a class-level and
* method-level annotation within the same class. In such scenarios, the
* {@code @DirtiesContext} may be used as a class-level and method-level
* annotation within the same class. In such scenarios, the
* {@code ApplicationContext} will be marked as <em>dirty</em> after any
* such annotated method as well as after the entire class. If the
* {@link ClassMode} is set to {@link ClassMode#AFTER_EACH_TEST_METHOD
@@ -53,16 +53,19 @@ import java.lang.annotation.Target;
* @author Sam Brannen
* @author Rod Johnson
* @since 2.0
* @see org.springframework.test.context.ContextConfiguration
*/
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface DirtiesContext {
/**
* Defines <i>modes</i> which determine how {@code &#064;DirtiesContext}
* is interpreted when used to annotate a test class.
* Defines <i>modes</i> which determine how {@code @DirtiesContext} is
* interpreted when used to annotate a test class.
*
* @since 3.0
*/
static enum ClassMode {
@@ -76,18 +79,64 @@ public @interface DirtiesContext {
* The associated {@code ApplicationContext} will be marked as
* <em>dirty</em> after each test method in the class.
*/
AFTER_EACH_TEST_METHOD
AFTER_EACH_TEST_METHOD;
}
/**
* Defines <i>modes</i> which determine how the context cache is cleared
* when {@code @DirtiesContext} is used in a test whose context is
* configured as part of a hierarchy via
* {@link org.springframework.test.context.ContextHierarchy @ContextHierarchy}.
*
* @since 3.2.2
*/
static enum HierarchyMode {
/**
* The context cache will be cleared using an <em>exhaustive</em> algorithm
* that includes not only the {@linkplain HierarchyMode#CURRENT_LEVEL current level}
* but also all other context hierarchies that share an ancestor context
* common to the current test.
*
* <p>All {@code ApplicationContexts} that reside in a subhierarchy of
* the common ancestor context will be removed from the context cache and
* closed.
*/
EXHAUSTIVE,
/**
* The {@code ApplicationContext} for the <em>current level</em> in the
* context hierarchy and all contexts in subhierarchies of the current
* level will be removed from the context cache and closed.
*
* <p>The <em>current level</em> refers to the {@code ApplicationContext}
* at the lowest level in the context hierarchy that is visible from the
* current test.
*/
CURRENT_LEVEL;
}
/**
* The <i>mode</i> to use when a test class is annotated with
* {@code &#064;DirtiesContext}.
* {@code @DirtiesContext}.
* <p>Defaults to {@link ClassMode#AFTER_CLASS AFTER_CLASS}.
* <p>Note: Setting the class mode on an annotated test method has no meaning,
* since the mere presence of the {@code &#064;DirtiesContext}
* annotation on a test method is sufficient.
* since the mere presence of the {@code @DirtiesContext} annotation on a
* test method is sufficient.
*
* @since 3.0
*/
ClassMode classMode() default ClassMode.AFTER_CLASS;
/**
* The context cache clearing <em>mode</em> to use when a context is
* configured as part of a hierarchy via
* {@link org.springframework.test.context.ContextHierarchy @ContextHierarchy}.
* <p>Defaults to {@link HierarchyMode#EXHAUSTIVE EXHAUSTIVE}.
*
* @since 3.2.2
*/
HierarchyMode hierarchyMode() default HierarchyMode.EXHAUSTIVE;
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright 2002-2013 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;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
/**
* {@code CacheAwareContextLoaderDelegate} loads application contexts from
* {@link MergedContextConfiguration} by delegating to the
* {@link ContextLoader} configured in the {@code MergedContextConfiguration}
* and interacting transparently with the {@link ContextCache} behind the scenes.
*
* <p>Note: {@code CacheAwareContextLoaderDelegate} does not implement the
* {@link ContextLoader} or {@link SmartContextLoader} interface.
*
* @author Sam Brannen
* @since 3.2.2
*/
public class CacheAwareContextLoaderDelegate {
private static final Log logger = LogFactory.getLog(CacheAwareContextLoaderDelegate.class);
private final ContextCache contextCache;
CacheAwareContextLoaderDelegate(ContextCache contextCache) {
Assert.notNull(contextCache, "ContextCache must not be null");
this.contextCache = contextCache;
}
/**
* Load the {@code ApplicationContext} for the supplied merged context
* configuration. Supports both the {@link SmartContextLoader} and
* {@link ContextLoader} SPIs.
* @throws Exception if an error occurs while loading the application context
*/
private ApplicationContext loadContextInternal(MergedContextConfiguration mergedContextConfiguration)
throws Exception {
ContextLoader contextLoader = mergedContextConfiguration.getContextLoader();
Assert.notNull(contextLoader, "Cannot load an ApplicationContext with a NULL 'contextLoader'. "
+ "Consider annotating your test class with @ContextConfiguration or @ContextHierarchy.");
ApplicationContext applicationContext;
if (contextLoader instanceof SmartContextLoader) {
SmartContextLoader smartContextLoader = (SmartContextLoader) contextLoader;
applicationContext = smartContextLoader.loadContext(mergedContextConfiguration);
}
else {
String[] locations = mergedContextConfiguration.getLocations();
Assert.notNull(locations, "Cannot load an ApplicationContext with a NULL 'locations' array. "
+ "Consider annotating your test class with @ContextConfiguration or @ContextHierarchy.");
applicationContext = contextLoader.loadContext(locations);
}
return applicationContext;
}
/**
* Load the {@link ApplicationContext application context} for the supplied
* merged context configuration.
*
* <p>If the context is present in the cache it will simply be returned;
* otherwise, it will be loaded, stored in the cache, and returned.
* @return the application context
* @throws IllegalStateException if an error occurs while retrieving or
* loading the application context
*/
public ApplicationContext loadContext(MergedContextConfiguration mergedContextConfiguration) {
synchronized (contextCache) {
ApplicationContext context = contextCache.get(mergedContextConfiguration);
if (context == null) {
try {
context = loadContextInternal(mergedContextConfiguration);
if (logger.isDebugEnabled()) {
logger.debug(String.format("Storing ApplicationContext in cache under key [%s].",
mergedContextConfiguration));
}
contextCache.put(mergedContextConfiguration, context);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to load ApplicationContext", ex);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug(String.format("Retrieved ApplicationContext from cache with key [%s].",
mergedContextConfiguration));
}
}
return context;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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,24 +16,30 @@
package org.springframework.test.context;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.style.ToStringCreator;
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
import org.springframework.util.Assert;
/**
* Cache for Spring {@link ApplicationContext ApplicationContexts} in a test environment.
*
* <p>Maintains a cache of {@link ApplicationContext contexts} keyed by
* {@link MergedContextConfiguration} instances. This has significant performance
* benefits if initializing the context would take time. While initializing a
* Spring context itself is very quick, some beans in a context, such as a
* {@code LocalSessionFactoryBean} for working with Hibernate, may take some time
* to initialize. Hence it often makes sense to perform that initialization only
* once per test suite.
* <p>Maintains a cache of {@code ApplicationContexts} keyed by
* {@link MergedContextConfiguration} instances.
*
* <p>This has significant performance benefits if initializing the context would take time.
* While initializing a Spring context itself is very quick, some beans in a context, such
* as a {@code LocalSessionFactoryBean} for working with Hibernate, may take some time to
* initialize. Hence it often makes sense to perform that initialization only once per
* test suite.
*
* @author Sam Brannen
* @author Juergen Hoeller
@@ -41,11 +47,22 @@ import org.springframework.util.Assert;
*/
class ContextCache {
private final Object monitor = new Object();
/**
* Map of context keys to Spring ApplicationContext instances.
* Map of context keys to Spring {@code ApplicationContext} instances.
*/
private final Map<MergedContextConfiguration, ApplicationContext> contextMap =
new ConcurrentHashMap<MergedContextConfiguration, ApplicationContext>(64);
private final Map<MergedContextConfiguration, ApplicationContext> contextMap = new ConcurrentHashMap<MergedContextConfiguration, ApplicationContext>(
64);
/**
* Map of parent keys to sets of children keys, representing a top-down <em>tree</em>
* of context hierarchies. This information is used for determining which subtrees
* need to be recursively removed and closed when removing a context that is a parent
* of other contexts.
*/
private final Map<MergedContextConfiguration, Set<MergedContextConfiguration>> hierarchyMap = new ConcurrentHashMap<MergedContextConfiguration, Set<MergedContextConfiguration>>(
64);
private int hitCount;
@@ -53,15 +70,18 @@ class ContextCache {
/**
* Clears all contexts from the cache.
* Clears all contexts from the cache and clears context hierarchy information as
* well.
*/
void clear() {
this.contextMap.clear();
synchronized (monitor) {
this.contextMap.clear();
this.hierarchyMap.clear();
}
}
/**
* Clears hit and miss count statistics for the cache (i.e., resets counters
* to zero).
* Clears hit and miss count statistics for the cache (i.e., resets counters to zero).
*/
void clearStatistics() {
this.hitCount = 0;
@@ -70,124 +90,210 @@ class ContextCache {
/**
* Return whether there is a cached context for the given key.
*
* @param key the context key (never {@code null})
*/
boolean contains(MergedContextConfiguration key) {
Assert.notNull(key, "Key must not be null");
return this.contextMap.containsKey(key);
synchronized (monitor) {
return this.contextMap.containsKey(key);
}
}
/**
* Obtain a cached ApplicationContext for the given key.
* <p>The {@link #getHitCount() hit} and {@link #getMissCount() miss}
* counts will be updated accordingly.
* Obtain a cached {@code ApplicationContext} for the given key.
*
* <p>The {@link #getHitCount() hit} and {@link #getMissCount() miss} counts will be
* updated accordingly.
*
* @param key the context key (never {@code null})
* @return the corresponding ApplicationContext instance,
* or {@code null} if not found in the cache.
* @return the corresponding {@code ApplicationContext} instance, or {@code null} if
* not found in the cache.
* @see #remove
*/
ApplicationContext get(MergedContextConfiguration key) {
Assert.notNull(key, "Key must not be null");
ApplicationContext context = this.contextMap.get(key);
if (context == null) {
incrementMissCount();
synchronized (monitor) {
ApplicationContext context = this.contextMap.get(key);
if (context == null) {
incrementMissCount();
}
else {
incrementHitCount();
}
return context;
}
else {
incrementHitCount();
}
return context;
}
/**
* Increment the hit count by one. A <em>hit</em> is an access to the
* cache, which returned a non-null context for a queried key.
* Increment the hit count by one. A <em>hit</em> is an access to the cache, which
* returned a non-null context for a queried key.
*/
private void incrementHitCount() {
this.hitCount++;
}
/**
* Increment the miss count by one. A <em>miss</em> is an access to the
* cache, which returned a {@code null} context for a queried key.
* Increment the miss count by one. A <em>miss</em> is an access to the cache, which
* returned a {@code null} context for a queried key.
*/
private void incrementMissCount() {
this.missCount++;
}
/**
* Get the overall hit count for this cache. A <em>hit</em> is an access
* to the cache, which returned a non-null context for a queried key.
* Get the overall hit count for this cache. A <em>hit</em> is an access to the cache,
* which returned a non-null context for a queried key.
*/
int getHitCount() {
return this.hitCount;
}
/**
* Get the overall miss count for this cache. A <em>miss</em> is an
* access to the cache, which returned a {@code null} context for a
* queried key.
* Get the overall miss count for this cache. A <em>miss</em> is an access to the
* cache, which returned a {@code null} context for a queried key.
*/
int getMissCount() {
return this.missCount;
}
/**
* Explicitly add an ApplicationContext instance to the cache under the given key.
* Explicitly add an {@code ApplicationContext} instance to the cache under the given
* key.
*
* @param key the context key (never {@code null})
* @param context the ApplicationContext instance (never {@code null})
* @param context the {@code ApplicationContext} instance (never {@code null})
*/
void put(MergedContextConfiguration key, ApplicationContext context) {
Assert.notNull(key, "Key must not be null");
Assert.notNull(context, "ApplicationContext must not be null");
this.contextMap.put(key, context);
}
/**
* Remove the context with the given key.
* @param key the context key (never {@code null})
* @return the corresponding ApplicationContext instance, or {@code null}
* if not found in the cache.
* @see #setDirty
*/
ApplicationContext remove(MergedContextConfiguration key) {
return this.contextMap.remove(key);
}
synchronized (monitor) {
this.contextMap.put(key, context);
/**
* Mark the context with the given key as dirty, effectively
* {@link #remove removing} the context from the cache and explicitly
* {@link ConfigurableApplicationContext#close() closing} it if it is an
* instance of {@link ConfigurableApplicationContext}.
* <p>Generally speaking, you would only call this method if you change the
* state of a singleton bean, potentially affecting future interaction with
* the context.
* @param key the context key (never {@code null})
* @see #remove
*/
void setDirty(MergedContextConfiguration key) {
Assert.notNull(key, "Key must not be null");
ApplicationContext context = remove(key);
if (context instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) context).close();
MergedContextConfiguration child = key;
MergedContextConfiguration parent = child.getParent();
while (parent != null) {
Set<MergedContextConfiguration> list = hierarchyMap.get(parent);
if (list == null) {
list = new HashSet<MergedContextConfiguration>();
hierarchyMap.put(parent, list);
}
list.add(child);
child = parent;
parent = child.getParent();
}
}
}
/**
* Determine the number of contexts currently stored in the cache. If the
* cache contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
* <tt>Integer.MAX_VALUE</tt>.
* Remove the context with the given key from the cache and explicitly
* {@linkplain ConfigurableApplicationContext#close() close} it if it is an
* instance of {@link ConfigurableApplicationContext}.
*
* <p>Generally speaking, you would only call this method if you change the
* state of a singleton bean, potentially affecting future interaction with
* the context.
*
* <p>In addition, the semantics of the supplied {@code HierarchyMode} will
* be honored. See the Javadoc for {@link HierarchyMode} for details.
*
* @param key the context key; never {@code null}
* @param hierarchyMode the hierarchy mode; may be {@code null} if the context
* is not part of a hierarchy
*/
int size() {
return this.contextMap.size();
void remove(MergedContextConfiguration key, HierarchyMode hierarchyMode) {
Assert.notNull(key, "Key must not be null");
// startKey is the level at which to begin clearing the cache, depending
// on the configured hierarchy mode.
MergedContextConfiguration startKey = key;
if (hierarchyMode == HierarchyMode.EXHAUSTIVE) {
while (startKey.getParent() != null) {
startKey = startKey.getParent();
}
}
synchronized (monitor) {
final List<MergedContextConfiguration> removedContexts = new ArrayList<MergedContextConfiguration>();
remove(removedContexts, startKey);
// Remove all remaining references to any removed contexts from the
// hierarchy map.
for (MergedContextConfiguration currentKey : removedContexts) {
for (Set<MergedContextConfiguration> children : hierarchyMap.values()) {
children.remove(currentKey);
}
}
// Remove empty entries from the hierarchy map.
for (MergedContextConfiguration currentKey : hierarchyMap.keySet()) {
if (hierarchyMap.get(currentKey).isEmpty()) {
hierarchyMap.remove(currentKey);
}
}
}
}
private void remove(List<MergedContextConfiguration> removedContexts, MergedContextConfiguration key) {
Assert.notNull(key, "Key must not be null");
synchronized (monitor) {
Set<MergedContextConfiguration> children = hierarchyMap.get(key);
if (children != null) {
for (MergedContextConfiguration child : children) {
// Recurse through lower levels
remove(removedContexts, child);
}
// Remove the set of children for the current context from the
// hierarchy map.
hierarchyMap.remove(key);
}
// Physically remove and close leaf nodes first (i.e., on the way back up the
// stack as opposed to prior to the recursive call).
ApplicationContext context = contextMap.remove(key);
if (context instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) context).close();
}
removedContexts.add(key);
}
}
/**
* Generates a text string, which contains the {@link #size() size} as well
* as the {@link #hitCount hit} and {@link #missCount miss} counts.
* Determine the number of contexts currently stored in the cache. If the cache
* contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
* <tt>Integer.MAX_VALUE</tt>.
*/
int size() {
synchronized (monitor) {
return this.contextMap.size();
}
}
/**
* Determine the number of parent contexts currently tracked within the cache.
*/
int getParentContextCount() {
synchronized (monitor) {
return this.hierarchyMap.size();
}
}
/**
* Generates a text string, which contains the {@linkplain #size() size} as well
* as the {@linkplain #getHitCount() hit}, {@linkplain #getMissCount() miss}, and
* {@linkplain #getParentContextCount() parent context} counts.
*/
@Override
public String toString() {
return new ToStringCreator(this).append("size", size()).append("hitCount", getHitCount()).
append("missCount", getMissCount()).toString();
return new ToStringCreator(this)//
.append("size", size())//
.append("hitCount", getHitCount())//
.append("missCount", getMissCount())//
.append("parentContextCount", getParentContextCount())//
.toString();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -58,7 +58,7 @@ import org.springframework.context.ConfigurableApplicationContext;
* {@link org.springframework.context.annotation.Bean @Bean}-methods</li>
* </ul>
*
* Consult the Javadoc for
* <p>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
@@ -66,11 +66,12 @@ import org.springframework.context.ConfigurableApplicationContext;
*
* @author Sam Brannen
* @since 2.5
* @see ContextHierarchy
* @see ActiveProfiles
* @see ContextLoader
* @see SmartContextLoader
* @see ContextConfigurationAttributes
* @see MergedContextConfiguration
* @see ActiveProfiles
* @see org.springframework.context.ApplicationContext
*/
@Documented
@@ -269,18 +270,43 @@ public @interface ContextConfiguration {
* explicit loader. If no class in the hierarchy specifies an explicit
* loader, a default loader will be used instead.
*
* <p>The default concrete implementation chosen at runtime will be
* <p>The default concrete implementation chosen at runtime will be either
* {@link org.springframework.test.context.support.DelegatingSmartContextLoader
* DelegatingSmartContextLoader}. For further details on the default behavior
* of various concrete {@code ContextLoaders}, check out the Javadoc for
* DelegatingSmartContextLoader} or
* {@link org.springframework.test.context.web.WebDelegatingSmartContextLoader
* WebDelegatingSmartContextLoader} depending on the absence or presence of
* {@link org.springframework.test.context.web.WebAppConfiguration
* &#064;WebAppConfiguration}. For further details on the default behavior
* of various concrete {@code SmartContextLoaders}, check out the Javadoc for
* {@link org.springframework.test.context.support.AbstractContextLoader
* AbstractContextLoader},
* {@link org.springframework.test.context.support.GenericXmlContextLoader
* GenericXmlContextLoader}, and
* GenericXmlContextLoader},
* {@link org.springframework.test.context.support.AnnotationConfigContextLoader
* AnnotationConfigContextLoader}.
* AnnotationConfigContextLoader},
* {@link org.springframework.test.context.web.GenericXmlWebContextLoader
* GenericXmlWebContextLoader}, and
* {@link org.springframework.test.context.web.AnnotationConfigWebContextLoader
* AnnotationConfigWebContextLoader}.
*
* @since 2.5
*/
Class<? extends ContextLoader> loader() default ContextLoader.class;
/**
* The name of the context hierarchy level represented by this configuration.
*
* <p>If not specified the name will be inferred based on the numerical level
* within all declared contexts within the hierarchy.
*
* <p>This attribute is only applicable when used within a test class hierarchy
* that is configured using {@code @ContextHierarchy}, in which case the name
* can be used for <em>merging</em> or <em>overriding</em> this configuration
* with configuration of the same name in hierarchy levels defined in superclasses.
* See the Javadoc for {@link ContextHierarchy @ContextHierarchy} for details.
*
* @since 3.2.2
*/
String name() default "";
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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,6 +16,8 @@
package org.springframework.test.context;
import java.util.Arrays;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -24,6 +26,7 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* {@code ContextConfigurationAttributes} encapsulates the context
@@ -54,6 +57,8 @@ public class ContextConfigurationAttributes {
private final boolean inheritInitializers;
private final String name;
/**
* Resolve resource locations from the {@link ContextConfiguration#locations() locations}
@@ -75,7 +80,8 @@ 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;
}
@@ -92,7 +98,7 @@ public class ContextConfigurationAttributes {
public ContextConfigurationAttributes(Class<?> declaringClass, ContextConfiguration contextConfiguration) {
this(declaringClass, resolveLocations(declaringClass, contextConfiguration), contextConfiguration.classes(),
contextConfiguration.inheritLocations(), contextConfiguration.initializers(),
contextConfiguration.inheritInitializers(), contextConfiguration.loader());
contextConfiguration.inheritInitializers(), contextConfiguration.name(), contextConfiguration.loader());
}
/**
@@ -109,13 +115,13 @@ public class ContextConfigurationAttributes {
* @throws IllegalArgumentException if the {@code declaringClass} or {@code contextLoaderClass} is
* {@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)}
* {@link #ContextConfigurationAttributes(Class, String[], Class[], boolean, Class[], boolean, String, 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);
this(declaringClass, locations, classes, inheritLocations, null, true, null, contextLoaderClass);
}
/**
@@ -138,6 +144,31 @@ public class ContextConfigurationAttributes {
boolean inheritLocations,
Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>[] initializers,
boolean inheritInitializers, Class<? extends ContextLoader> contextLoaderClass) {
this(declaringClass, locations, classes, inheritLocations, initializers, inheritInitializers, null,
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 name the name of level in the context hierarchy, or {@code null} if not applicable
* @param contextLoaderClass the {@code ContextLoader} class declared via {@code @ContextConfiguration}
* @throws IllegalArgumentException if the {@code declaringClass} or {@code contextLoaderClass} is
* {@code null}, or if the {@code locations} and {@code classes} are both non-empty
*/
public ContextConfigurationAttributes(Class<?> declaringClass, String[] locations, Class<?>[] classes,
boolean inheritLocations,
Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>[] initializers,
boolean inheritInitializers, String name, Class<? extends ContextLoader> contextLoaderClass) {
Assert.notNull(declaringClass, "declaringClass must not be null");
Assert.notNull(contextLoaderClass, "contextLoaderClass must not be null");
@@ -158,6 +189,7 @@ public class ContextConfigurationAttributes {
this.inheritLocations = inheritLocations;
this.initializers = initializers;
this.inheritInitializers = inheritInitializers;
this.name = StringUtils.hasText(name) ? name : null;
this.contextLoaderClass = contextLoaderClass;
}
@@ -305,6 +337,101 @@ public class ContextConfigurationAttributes {
return contextLoaderClass;
}
/**
* Get the name of the context hierarchy level that was declared via
* {@link ContextConfiguration @ContextConfiguration}.
*
* @return the name of the context hierarchy level or {@code null} if not applicable
* @see ContextConfiguration#name()
* @since 3.2.2
*/
public String getName() {
return this.name;
}
/**
* Generate a unique hash code for all properties of this
* {@code ContextConfigurationAttributes} instance excluding the
* {@linkplain #getName() name}.
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + declaringClass.hashCode();
result = prime * result + Arrays.hashCode(locations);
result = prime * result + Arrays.hashCode(classes);
result = prime * result + (inheritLocations ? 1231 : 1237);
result = prime * result + Arrays.hashCode(initializers);
result = prime * result + (inheritInitializers ? 1231 : 1237);
result = prime * result + contextLoaderClass.hashCode();
return result;
}
/**
* Determine if the supplied object is equal to this
* {@code ContextConfigurationAttributes} instance by comparing both object's
* {@linkplain #getDeclaringClass() declaring class},
* {@linkplain #getLocations() locations},
* {@linkplain #getClasses() annotated classes},
* {@linkplain #isInheritLocations() inheritLocations flag},
* {@linkplain #getInitializers() context initializer classes},
* {@linkplain #isInheritInitializers() inheritInitializers flag}, and the
* {@link #getContextLoaderClass() ContextLoader class}.
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ContextConfigurationAttributes)) {
return false;
}
final ContextConfigurationAttributes that = (ContextConfigurationAttributes) obj;
if (this.declaringClass == null) {
if (that.declaringClass != null) {
return false;
}
}
else if (!this.declaringClass.equals(that.declaringClass)) {
return false;
}
if (!Arrays.equals(this.locations, that.locations)) {
return false;
}
if (!Arrays.equals(this.classes, that.classes)) {
return false;
}
if (this.inheritLocations != that.inheritLocations) {
return false;
}
if (!Arrays.equals(this.initializers, that.initializers)) {
return false;
}
if (this.inheritInitializers != that.inheritInitializers) {
return false;
}
if (this.contextLoaderClass == null) {
if (that.contextLoaderClass != null) {
return false;
}
}
else if (!this.contextLoaderClass.equals(that.contextLoaderClass)) {
return false;
}
return true;
}
/**
* Provide a String representation of the context configuration attributes
* and declaring class.
@@ -318,6 +445,7 @@ public class ContextConfigurationAttributes {
.append("inheritLocations", inheritLocations)//
.append("initializers", ObjectUtils.nullSafeToString(initializers))//
.append("inheritInitializers", inheritInitializers)//
.append("name", name)//
.append("contextLoaderClass", contextLoaderClass.getName())//
.toString();
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright 2002-2013 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;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* {@code @ContextHierarchy} is a class-level annotation that is used to define
* a hierarchy of {@link org.springframework.context.ApplicationContext
* ApplicationContexts} for integration tests.
*
* <h3>Examples</h3>
* <p>The following JUnit-based examples demonstrate common configuration
* scenarios for integration tests that require the use of context hierarchies.
*
* <h4>Single Test Class with Context Hierarchy</h4>
* <p>{@code ControllerIntegrationTests} represents a typical integration testing
* scenario for a Spring MVC web application by declaring a context hierarchy
* consisting of two levels, one for the <em>root</em> {@code WebApplicationContext}
* (with {@code TestAppConfig}) and one for the <em>dispatcher servlet</em>
* {@code WebApplicationContext} (with {@code WebConfig}). The {@code
* WebApplicationContext} that is <em>autowired</em> into the test instance is
* the one for the child context (i.e., the lowest context in the hierarchy).
*
* <pre class="code">
* &#064;RunWith(SpringJUnit4ClassRunner.class)
* &#064;WebAppConfiguration
* &#064;ContextHierarchy({
* &#064;ContextConfiguration(classes = TestAppConfig.class),
* &#064;ContextConfiguration(classes = WebConfig.class)
* })
* public class ControllerIntegrationTests {
*
* &#064;Autowired
* private WebApplicationContext wac;
*
* // ...
* }</pre>
*
* <h4>Class Hierarchy with Implicit Parent Context</h4>
* <p>The following test classes define a context hierarchy within a test class
* hierarchy. {@code AbstractWebTests} declares the configuration for a root
* {@code WebApplicationContext} in a Spring-powered web application. Note,
* however, that {@code AbstractWebTests} does not declare {@code @ContextHierarchy};
* consequently, subclasses of {@code AbstractWebTests} can optionally participate
* in a context hierarchy or follow the standard semantics for {@code @ContextConfiguration}.
* {@code SoapWebServiceTests} and {@code RestWebServiceTests} both extend
* {@code AbstractWebTests} and define a context hierarchy via {@code @ContextHierarchy}.
* The result is that three application contexts will be loaded (one for each
* declaration of {@code @ContextConfiguration}, and the application context
* loaded based on the configuration in {@code AbstractWebTests} will be set as
* the parent context for each of the contexts loaded for the concrete subclasses.
*
* <pre class="code">
* &#064;RunWith(SpringJUnit4ClassRunner.class)
* &#064;WebAppConfiguration
* &#064;ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml")
* public abstract class AbstractWebTests {}
*
* &#064;ContextHierarchy(&#064;ContextConfiguration("/spring/soap-ws-config.xml")
* public class SoapWebServiceTests extends AbstractWebTests {}
*
* &#064;ContextHierarchy(&#064;ContextConfiguration("/spring/rest-ws-config.xml")
* public class RestWebServiceTests extends AbstractWebTests {}</pre>
*
* <h4>Class Hierarchy with Merged Context Hierarchy Configuration</h4>
* <p>The following classes demonstrate the use of <em>named</em> hierarchy levels
* in order to <em>merge</em> the configuration for specific levels in a context
* hierarchy. {@code BaseTests} defines two levels in the hierarchy, {@code parent}
* and {@code child}. {@code ExtendedTests} extends {@code BaseTests} and instructs
* the Spring TestContext Framework to merge the context configuration for the
* {@code child} hierarchy level, simply by ensuring that the names declared via
* {@link ContextConfiguration#name} are both {@code "child"}. The result is that
* three application contexts will be loaded: one for {@code "/app-config.xml"},
* one for {@code "/user-config.xml"}, and one for <code>{"/user-config.xml",
* "/order-config.xml"}</code>. As with the previous example, the application
* context loaded from {@code "/app-config.xml"} will be set as the parent context
* for the contexts loaded from {@code "/user-config.xml"} and <code>{"/user-config.xml",
* "/order-config.xml"}</code>.
*
* <pre class="code">
* &#064;RunWith(SpringJUnit4ClassRunner.class)
* &#064;ContextHierarchy({
* &#064;ContextConfiguration(name = "parent", locations = "/app-config.xml"),
* &#064;ContextConfiguration(name = "child", locations = "/user-config.xml")
* })
* public class BaseTests {}
*
* &#064;ContextHierarchy(
* &#064;ContextConfiguration(name = "child", locations = "/order-config.xml")
* )
* public class ExtendedTests extends BaseTests {}</pre>
*
* <h4>Class Hierarchy with Overridden Context Hierarchy Configuration</h4>
* <p>In contrast to the previous example, this example demonstrates how to
* <em>override</em> the configuration for a given named level in a context hierarchy
* by setting the {@link ContextConfiguration#inheritLocations} flag to {@code false}.
* Consequently, the application context for {@code ExtendedTests} will be loaded
* only from {@code "/test-user-config.xml"} and will have its parent set to the
* context loaded from {@code "/app-config.xml"}.
*
* <pre class="code">
* &#064;RunWith(SpringJUnit4ClassRunner.class)
* &#064;ContextHierarchy({
* &#064;ContextConfiguration(name = "parent", locations = "/app-config.xml"),
* &#064;ContextConfiguration(name = "child", locations = "/user-config.xml")
* })
* public class BaseTests {}
*
* &#064;ContextHierarchy(
* &#064;ContextConfiguration(name = "child", locations = "/test-user-config.xml", inheritLocations=false)
* )
* public class ExtendedTests extends BaseTests {}</pre>
*
* @author Sam Brannen
* @since 3.2.2
* @see ContextConfiguration
* @see org.springframework.context.ApplicationContext
*/
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ContextHierarchy {
/**
* A list of {@link ContextConfiguration @ContextConfiguration} instances,
* each of which defines a level in the context hierarchy.
*
* <p>If you need to merge or override the configuration for a given level
* of the context hierarchy within a test class hierarchy, you must explicitly
* name that level by supplying the same value to the {@link ContextConfiguration#name
* name} attribute in {@code @ContextConfiguration} at each level in the
* class hierarchy. See the class-level Javadoc for examples.
*/
ContextConfiguration[] value();
}

View File

@@ -16,20 +16,24 @@
package org.springframework.test.context;
import static org.springframework.beans.BeanUtils.*;
import static org.springframework.core.annotation.AnnotationUtils.*;
import static org.springframework.beans.BeanUtils.instantiateClass;
import static org.springframework.core.annotation.AnnotationUtils.findAnnotationDeclaringClass;
import static org.springframework.core.annotation.AnnotationUtils.findAnnotationDeclaringClassForTypes;
import static org.springframework.core.annotation.AnnotationUtils.isAnnotationDeclaredLocally;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
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.core.annotation.AnnotationUtils;
@@ -52,10 +56,13 @@ import org.springframework.util.StringUtils;
* @see ContextConfigurationAttributes
* @see ActiveProfiles
* @see ApplicationContextInitializer
* @see ContextHierarchy
* @see MergedContextConfiguration
*/
abstract class ContextLoaderUtils {
static final String GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX = "ContextHierarchyLevel#";
private static final Log logger = LogFactory.getLog(ContextLoaderUtils.class);
private static final String DEFAULT_CONTEXT_LOADER_CLASS_NAME = "org.springframework.test.context.support.DelegatingSmartContextLoader";
@@ -70,30 +77,29 @@ abstract class ContextLoaderUtils {
}
/**
* Resolve the {@link ContextLoader} {@linkplain Class class} to use for the
* supplied list of {@link ContextConfigurationAttributes} and then
* instantiate and return that {@code ContextLoader}.
* Resolve the {@link ContextLoader} {@linkplain Class class} to use for the supplied
* list of {@link ContextConfigurationAttributes} and then instantiate and return that
* {@code ContextLoader}.
*
* <p>If the supplied {@code defaultContextLoaderClassName} is
* {@code null} or <em>empty</em>, depending on the absence or presence
* of {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration}
* either {@value #DEFAULT_CONTEXT_LOADER_CLASS_NAME}
* or {@value #DEFAULT_WEB_CONTEXT_LOADER_CLASS_NAME} will be used as the
* default context loader class name. For details on the class resolution
* process, see {@link #resolveContextLoaderClass}.
* <p>If the supplied {@code defaultContextLoaderClassName} is {@code null} or
* <em>empty</em>, depending on the absence or presence of
* {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration} either
* {@code "org.springframework.test.context.support.DelegatingSmartContextLoader"} or
* {@code "org.springframework.test.context.web.WebDelegatingSmartContextLoader"} will
* be used as the default context loader class name. For details on the class
* resolution process, see {@link #resolveContextLoaderClass}.
*
* @param testClass the test class for which the {@code ContextLoader}
* should be resolved; must not be {@code null}
* @param configAttributesList the list of configuration attributes to process;
* must not be {@code null} or <em>empty</em>; must be ordered <em>bottom-up</em>
* @param testClass the test class for which the {@code ContextLoader} should be
* resolved; must not be {@code null}
* @param configAttributesList the list of configuration attributes to process; must
* not be {@code null} or <em>empty</em>; must be ordered <em>bottom-up</em>
* (i.e., as if we were traversing up the class hierarchy)
* @param defaultContextLoaderClassName the name of the default
* {@code ContextLoader} class to use; may be {@code null} or <em>empty</em>
* @return the resolved {@code ContextLoader} for the supplied
* {@code testClass} (never {@code null})
* @param defaultContextLoaderClassName the name of the default {@code ContextLoader}
* class to use; may be {@code null} or <em>empty</em>
* @return the resolved {@code ContextLoader} for the supplied {@code testClass}
* (never {@code null})
* @see #resolveContextLoaderClass
*/
@SuppressWarnings("javadoc")
static ContextLoader resolveContextLoader(Class<?> testClass,
List<ContextConfigurationAttributes> configAttributesList, String defaultContextLoaderClassName) {
Assert.notNull(testClass, "Class must not be null");
@@ -113,36 +119,35 @@ abstract class ContextLoaderUtils {
}
/**
* Resolve the {@link ContextLoader} {@linkplain Class class} to use for the
* supplied list of {@link ContextConfigurationAttributes}.
* Resolve the {@link ContextLoader} {@linkplain Class class} to use for the supplied
* list of {@link ContextConfigurationAttributes}.
*
* <p>Beginning with the first level in the context configuration attributes
* hierarchy:
* <p>Beginning with the first level in the context configuration attributes hierarchy:
*
* <ol>
* <li>If the {@link ContextConfigurationAttributes#getContextLoaderClass()
* contextLoaderClass} property of {@link ContextConfigurationAttributes} is
* configured with an explicit class, that class will be returned.</li>
* <li>If an explicit {@code ContextLoader} class is not specified at the
* current level in the hierarchy, traverse to the next level in the hierarchy
* and return to step #1.</li>
* <li>If no explicit {@code ContextLoader} class is found after traversing
* the hierarchy, an attempt will be made to load and return the class
* with the supplied {@code defaultContextLoaderClassName}.</li>
* <li>If an explicit {@code ContextLoader} class is not specified at the current
* level in the hierarchy, traverse to the next level in the hierarchy and return to
* step #1.</li>
* <li>If no explicit {@code ContextLoader} class is found after traversing the
* hierarchy, an attempt will be made to load and return the class with the supplied
* {@code defaultContextLoaderClassName}.</li>
* </ol>
*
* @param testClass the class for which to resolve the {@code ContextLoader}
* class; must not be {@code null}; only used for logging purposes
* @param configAttributesList the list of configuration attributes to process;
* must not be {@code null} or <em>empty</em>; must be ordered <em>bottom-up</em>
* @param testClass the class for which to resolve the {@code ContextLoader} class;
* must not be {@code null}; only used for logging purposes
* @param configAttributesList the list of configuration attributes to process; must
* not be {@code null} or <em>empty</em>; must be ordered <em>bottom-up</em>
* (i.e., as if we were traversing up the class hierarchy)
* @param defaultContextLoaderClassName the name of the default
* {@code ContextLoader} class to use; must not be {@code null} or empty
* @param defaultContextLoaderClassName the name of the default {@code ContextLoader}
* class to use; must not be {@code null} or empty
* @return the {@code ContextLoader} class to use for the supplied test class
* @throws IllegalArgumentException if {@code @ContextConfiguration} is not
* <em>present</em> on the supplied test class
* @throws IllegalStateException if the default {@code ContextLoader} class
* could not be loaded
* @throws IllegalStateException if the default {@code ContextLoader} class could not
* be loaded
*/
@SuppressWarnings("unchecked")
static Class<? extends ContextLoader> resolveContextLoaderClass(Class<?> testClass,
@@ -184,22 +189,201 @@ abstract class ContextLoaderUtils {
}
/**
* Resolve the list of {@link ContextConfigurationAttributes configuration
* attributes} for the supplied {@link Class class} and its superclasses.
* Convenience method for creating a {@link ContextConfigurationAttributes} instance
* from the supplied {@link ContextConfiguration} and declaring class and then adding
* the attributes to the supplied list.
*/
private static void convertContextConfigToConfigAttributesAndAddToList(ContextConfiguration contextConfiguration,
Class<?> declaringClass, final List<ContextConfigurationAttributes> attributesList) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Retrieved @ContextConfiguration [%s] for declaring class [%s].",
contextConfiguration, declaringClass.getName()));
}
ContextConfigurationAttributes attributes = new ContextConfigurationAttributes(declaringClass,
contextConfiguration);
if (logger.isTraceEnabled()) {
logger.trace("Resolved context configuration attributes: " + attributes);
}
attributesList.add(attributes);
}
/**
* Resolve the list of lists of {@linkplain ContextConfigurationAttributes context
* configuration attributes} for the supplied {@linkplain Class test class} and its
* superclasses, taking into account context hierarchies declared via
* {@link ContextHierarchy @ContextHierarchy} and
* {@link ContextConfiguration @ContextConfiguration}.
*
* <p>Note that the {@link ContextConfiguration#inheritLocations
* 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.
* <p>The outer list represents a top-down ordering of context configuration
* attributes, where each element in the list represents the context configuration
* declared on a given test class in the class hierarchy. Each nested list
* contains the context configuration attributes declared either via a single
* instance of {@code @ContextConfiguration} on the particular class or via
* multiple instances of {@code @ContextConfiguration} declared within a
* single {@code @ContextHierarchy} instance on the particular class.
* Furthermore, each nested list maintains the order in which
* {@code @ContextConfiguration} instances are declared.
*
* <p>Note that the {@link ContextConfiguration#inheritLocations inheritLocations} and
* {@link ContextConfiguration#inheritInitializers() inheritInitializers} flags of
* {@link ContextConfiguration @ContextConfiguration} will <strong>not</strong>
* be taken into consideration. If these flags need to be honored, that must be
* handled manually when traversing the nested lists returned by this method.
*
* @param testClass the class for which to resolve the context hierarchy attributes
* (must not be {@code null})
* @return the list of lists of configuration attributes for the specified class;
* never {@code null}
* @throws IllegalArgumentException if the supplied class is {@code null}; if
* neither {@code @ContextConfiguration} nor {@code @ContextHierarchy} is
* <em>present</em> on the supplied class; if a given class in the class hierarchy
* declares both {@code @ContextConfiguration} and {@code @ContextHierarchy} as
* top-level annotations; or if individual {@code @ContextConfiguration}
* elements within a {@code @ContextHierarchy} declaration on a given class
* in the class hierarchy do not define unique context configuration.
*
* @since 3.2.2
* @see #buildContextHierarchyMap(Class)
* @see #resolveContextConfigurationAttributes(Class)
*/
static List<List<ContextConfigurationAttributes>> resolveContextHierarchyAttributes(Class<?> testClass) {
Assert.notNull(testClass, "Class must not be null");
final Class<ContextConfiguration> contextConfigType = ContextConfiguration.class;
final Class<ContextHierarchy> contextHierarchyType = ContextHierarchy.class;
final List<Class<? extends Annotation>> annotationTypes = Arrays.asList(contextConfigType, contextHierarchyType);
final List<List<ContextConfigurationAttributes>> hierarchyAttributes = new ArrayList<List<ContextConfigurationAttributes>>();
Class<?> declaringClass = findAnnotationDeclaringClassForTypes(annotationTypes, testClass);
Assert.notNull(declaringClass, String.format(
"Could not find an 'annotation declaring class' for annotation type [%s] or [%s] and test class [%s]",
contextConfigType.getName(), contextHierarchyType.getName(), testClass.getName()));
while (declaringClass != null) {
boolean contextConfigDeclaredLocally = isAnnotationDeclaredLocally(contextConfigType, declaringClass);
boolean contextHierarchyDeclaredLocally = isAnnotationDeclaredLocally(contextHierarchyType, declaringClass);
if (contextConfigDeclaredLocally && contextHierarchyDeclaredLocally) {
String msg = String.format("Test class [%s] has been configured with both @ContextConfiguration "
+ "and @ContextHierarchy as class-level annotations. Only one of these annotations may "
+ "be declared as a top-level annotation per test class.", declaringClass.getName());
logger.error(msg);
throw new IllegalStateException(msg);
}
final List<ContextConfigurationAttributes> configAttributesList = new ArrayList<ContextConfigurationAttributes>();
if (contextConfigDeclaredLocally) {
ContextConfiguration contextConfiguration = declaringClass.getAnnotation(contextConfigType);
convertContextConfigToConfigAttributesAndAddToList(contextConfiguration, declaringClass,
configAttributesList);
}
else if (contextHierarchyDeclaredLocally) {
ContextHierarchy contextHierarchy = declaringClass.getAnnotation(contextHierarchyType);
for (ContextConfiguration contextConfiguration : contextHierarchy.value()) {
convertContextConfigToConfigAttributesAndAddToList(contextConfiguration, declaringClass,
configAttributesList);
}
// Check for uniqueness
Set<ContextConfigurationAttributes> configAttributesSet = new HashSet<ContextConfigurationAttributes>(
configAttributesList);
if (configAttributesSet.size() != configAttributesList.size()) {
String msg = String.format("The @ContextConfiguration elements configured via "
+ "@ContextHierarchy in test class [%s] must define unique contexts to load.",
declaringClass.getName());
logger.error(msg);
throw new IllegalStateException(msg);
}
}
else {
// This should theoretically actually never happen...
String msg = String.format("Test class [%s] has been configured with neither @ContextConfiguration "
+ "nor @ContextHierarchy as a class-level annotation.", declaringClass.getName());
logger.error(msg);
throw new IllegalStateException(msg);
}
hierarchyAttributes.add(0, configAttributesList);
declaringClass = findAnnotationDeclaringClassForTypes(annotationTypes, declaringClass.getSuperclass());
}
return hierarchyAttributes;
}
/**
* Build a <em>context hierarchy map</em> for the supplied {@linkplain Class
* test class} and its superclasses, taking into account context hierarchies
* declared via {@link ContextHierarchy @ContextHierarchy} and
* {@link ContextConfiguration @ContextConfiguration}.
*
* <p>Each value in the map represents the consolidated list of {@linkplain
* ContextConfigurationAttributes context configuration attributes} for a
* given level in the context hierarchy (potentially across the test class
* hierarchy), keyed by the {@link ContextConfiguration#name() name} of the
* context hierarchy level.
*
* <p>If a given level in the context hierarchy does not have an explicit
* name (i.e., configured via {@link ContextConfiguration#name}), a name will
* be generated for that hierarchy level by appending the numerical level to
* the {@link #GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX}.
*
* @param testClass the class for which to resolve the context hierarchy map
* (must not be {@code null})
* @return a map of context configuration attributes for the context hierarchy,
* keyed by context hierarchy level name; never {@code null}
*
* @since 3.2.2
* @see #resolveContextHierarchyAttributes(Class)
*/
static Map<String, List<ContextConfigurationAttributes>> buildContextHierarchyMap(Class<?> testClass) {
final Map<String, List<ContextConfigurationAttributes>> map = new LinkedHashMap<String, List<ContextConfigurationAttributes>>();
int hierarchyLevel = 1;
for (List<ContextConfigurationAttributes> configAttributesList : resolveContextHierarchyAttributes(testClass)) {
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
String name = configAttributes.getName();
// Assign a generated name?
if (!StringUtils.hasText(name)) {
name = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + hierarchyLevel;
}
// Encountered a new context hierarchy level?
if (!map.containsKey(name)) {
hierarchyLevel++;
map.put(name, new ArrayList<ContextConfigurationAttributes>());
}
map.get(name).add(configAttributes);
}
}
return map;
}
/**
* Resolve the list of {@linkplain ContextConfigurationAttributes context
* configuration attributes} for the supplied {@linkplain Class test class} and its
* superclasses.
*
* <p>Note that the {@link ContextConfiguration#inheritLocations inheritLocations} and
* {@link ContextConfiguration#inheritInitializers() inheritInitializers} flags of
* {@link ContextConfiguration @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 testClass the class for which to resolve the configuration attributes (must
* not be {@code null})
* @return the list of configuration attributes for the specified class, ordered <em>bottom-up</em>
* (i.e., as if we were traversing up the class hierarchy); never {@code null}
* @throws IllegalArgumentException if the supplied class is {@code null} or
* if {@code @ContextConfiguration} is not <em>present</em> on the supplied class
* @return the list of configuration attributes for the specified class, ordered
* <em>bottom-up</em> (i.e., as if we were traversing up the class hierarchy);
* never {@code null}
* @throws IllegalArgumentException if the supplied class is {@code null} or if
* {@code @ContextConfiguration} is not <em>present</em> on the supplied class
*/
static List<ContextConfigurationAttributes> resolveContextConfigurationAttributes(Class<?> testClass) {
Assert.notNull(testClass, "Class must not be null");
@@ -214,19 +398,7 @@ abstract class ContextLoaderUtils {
while (declaringClass != null) {
ContextConfiguration contextConfiguration = declaringClass.getAnnotation(annotationType);
if (logger.isTraceEnabled()) {
logger.trace(String.format("Retrieved @ContextConfiguration [%s] for declaring class [%s].",
contextConfiguration, declaringClass.getName()));
}
ContextConfigurationAttributes attributes = new ContextConfigurationAttributes(declaringClass,
contextConfiguration);
if (logger.isTraceEnabled()) {
logger.trace("Resolved context configuration attributes: " + attributes);
}
attributesList.add(attributes);
convertContextConfigToConfigAttributesAndAddToList(contextConfiguration, declaringClass, attributesList);
declaringClass = findAnnotationDeclaringClass(annotationType, declaringClass.getSuperclass());
}
@@ -234,22 +406,21 @@ abstract class ContextLoaderUtils {
}
/**
* Resolve the list of merged {@code ApplicationContextInitializer} classes
* for the supplied list of {@code ContextConfigurationAttributes}.
* 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} flag is
* set to {@code true} 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.
* consideration. Specifically, if the {@code inheritInitializers} flag is set to
* {@code true} 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} or <em>empty</em>; must be ordered <em>bottom-up</em>
* @param configAttributesList the list of configuration attributes to process; must
* not be {@code null} or <em>empty</em>; must be ordered <em>bottom-up</em>
* (i.e., as if we were traversing up the class hierarchy)
* @return the set of merged context initializer classes, including those
* from superclasses if appropriate (never {@code null})
* @return the set of merged context initializer classes, including those from
* superclasses if appropriate (never {@code null})
* @since 3.2
*/
static Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> resolveInitializerClasses(
@@ -278,16 +449,15 @@ abstract class ContextLoaderUtils {
/**
* Resolve <em>active bean definition profiles</em> for the supplied {@link Class}.
*
* <p>Note that the {@link ActiveProfiles#inheritProfiles inheritProfiles}
* flag of {@link ActiveProfiles &#064;ActiveProfiles} will be taken into
* consideration. Specifically, if the {@code inheritProfiles} flag is
* set to {@code true}, profiles defined in the test class will be
* merged with those defined in superclasses.
* <p>Note that the {@link ActiveProfiles#inheritProfiles inheritProfiles} flag of
* {@link ActiveProfiles @ActiveProfiles} will be taken into consideration.
* Specifically, if the {@code inheritProfiles} flag is set to {@code true}, profiles
* defined in the test class will be merged with those defined in superclasses.
*
* @param testClass the class for which to resolve the active profiles (must
* not be {@code null})
* @return the set of active profiles for the specified class, including
* active profiles from superclasses if appropriate (never {@code null})
* @param testClass the class for which to resolve the active profiles (must not be
* {@code null})
* @return the set of active profiles for the specified class, including active
* profiles from superclasses if appropriate (never {@code null})
* @see ActiveProfiles
* @see org.springframework.context.annotation.Profile
*/
@@ -342,14 +512,72 @@ abstract class ContextLoaderUtils {
}
/**
* Build the {@link MergedContextConfiguration merged context configuration}
* for the supplied {@link Class testClass} and
* {@code defaultContextLoaderClassName}.
* Build the {@link MergedContextConfiguration merged context configuration} for
* the supplied {@link Class testClass} and {@code defaultContextLoaderClassName},
* taking into account context hierarchies declared via
* {@link ContextHierarchy @ContextHierarchy} and
* {@link ContextConfiguration @ContextConfiguration}.
*
* @param testClass the test class for which the {@code MergedContextConfiguration}
* should be built (must not be {@code null})
* @param defaultContextLoaderClassName the name of the default
* {@code ContextLoader} class to use (may be {@code null})
* @param defaultContextLoaderClassName the name of the default {@code ContextLoader}
* class to use (may be {@code null})
* @param cacheAwareContextLoaderDelegate the cache-aware context loader delegate to
* be passed to the {@code MergedContextConfiguration} constructor
* @return the merged context configuration
* @see #buildContextHierarchyMap(Class)
* @see #buildMergedContextConfiguration(Class, List, String, MergedContextConfiguration, CacheAwareContextLoaderDelegate)
*/
@SuppressWarnings("javadoc")
static MergedContextConfiguration buildMergedContextConfiguration(Class<?> testClass,
String defaultContextLoaderClassName, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
if (testClass.isAnnotationPresent(ContextHierarchy.class)) {
Map<String, List<ContextConfigurationAttributes>> hierarchyMap = buildContextHierarchyMap(testClass);
MergedContextConfiguration parentConfig = null;
MergedContextConfiguration mergedConfig = null;
for (List<ContextConfigurationAttributes> list : hierarchyMap.values()) {
List<ContextConfigurationAttributes> reversedList = new ArrayList<ContextConfigurationAttributes>(list);
Collections.reverse(reversedList);
// Don't use the supplied testClass; instead ensure that we are
// building the MCC for the actual test class that declared the
// configuration for the current level in the context hierarchy.
Assert.notEmpty(reversedList, "ContextConfigurationAttributes list must not be empty");
Class<?> declaringClass = reversedList.get(0).getDeclaringClass();
mergedConfig = buildMergedContextConfiguration(declaringClass, reversedList,
defaultContextLoaderClassName, parentConfig, cacheAwareContextLoaderDelegate);
parentConfig = mergedConfig;
}
// Return the last level in the context hierarchy
return mergedConfig;
}
else {
return buildMergedContextConfiguration(testClass, resolveContextConfigurationAttributes(testClass),
defaultContextLoaderClassName, null, cacheAwareContextLoaderDelegate);
}
}
/**
* Build the {@link MergedContextConfiguration merged context configuration} for the
* supplied {@link Class testClass}, context configuration attributes,
* {@code defaultContextLoaderClassName}, and parent context configuration.
*
* @param testClass the test class for which the {@code MergedContextConfiguration}
* should be built (must not be {@code null})
* @param configAttributesList the list of context configuration attributes for the
* specified test class, ordered <em>bottom-up</em> (i.e., as if we were
* traversing up the class hierarchy); never {@code null} or empty
* @param defaultContextLoaderClassName the name of the default {@code ContextLoader}
* class to use (may be {@code null})
* @param parentConfig the merged context configuration for the parent application
* context in a context hierarchy, or {@code null} if there is no parent
* @param cacheAwareContextLoaderDelegate the cache-aware context loader delegate to
* be passed to the {@code MergedContextConfiguration} constructor
* @return the merged context configuration
* @see #resolveContextLoader
* @see #resolveContextConfigurationAttributes
@@ -358,10 +586,11 @@ abstract class ContextLoaderUtils {
* @see #resolveActiveProfiles
* @see MergedContextConfiguration
*/
static MergedContextConfiguration buildMergedContextConfiguration(Class<?> testClass,
String defaultContextLoaderClassName) {
private static MergedContextConfiguration buildMergedContextConfiguration(final Class<?> testClass,
final List<ContextConfigurationAttributes> configAttributesList,
final String defaultContextLoaderClassName, MergedContextConfiguration parentConfig,
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
final List<ContextConfigurationAttributes> configAttributesList = resolveContextConfigurationAttributes(testClass);
final ContextLoader contextLoader = resolveContextLoader(testClass, configAttributesList,
defaultContextLoaderClassName);
final List<String> locationsList = new ArrayList<String>();
@@ -397,22 +626,21 @@ abstract class ContextLoaderUtils {
String[] activeProfiles = resolveActiveProfiles(testClass);
MergedContextConfiguration mergedConfig = buildWebMergedContextConfiguration(testClass, locations, classes,
initializerClasses, activeProfiles, contextLoader);
initializerClasses, activeProfiles, contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
if (mergedConfig == null) {
mergedConfig = new MergedContextConfiguration(testClass, locations, classes, initializerClasses,
activeProfiles, contextLoader);
activeProfiles, contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
}
return mergedConfig;
}
/**
* Load the {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration}
* Load the {@link org.springframework.test.context.web.WebAppConfiguration}
* class, using reflection in order to avoid package cycles.
*
* @return the {@code @WebAppConfiguration} class or {@code null} if it
* cannot be loaded
* @return the {@code @WebAppConfiguration} class or {@code null} if it cannot be loaded
* @since 3.2
*/
@SuppressWarnings("unchecked")
@@ -431,12 +659,10 @@ abstract class ContextLoaderUtils {
}
/**
* Attempt to build a {@link org.springframework.test.context.web.WebMergedContextConfiguration
* WebMergedContextConfiguration} from the supplied arguments, using reflection
* in order to avoid package cycles.
* Attempt to build a {@link org.springframework.test.context.web.WebMergedContextConfiguration}
* from the supplied arguments, using reflection in order to avoid package cycles.
*
* @return the {@code WebMergedContextConfiguration} or {@code null} if
* it could not be built
* @return the {@code WebMergedContextConfiguration} or {@code null} if it could not be built
* @since 3.2
*/
@SuppressWarnings("unchecked")
@@ -445,7 +671,8 @@ abstract class ContextLoaderUtils {
String[] locations,
Class<?>[] classes,
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses,
String[] activeProfiles, ContextLoader contextLoader) {
String[] activeProfiles, ContextLoader contextLoader,
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parentConfig) {
Class<? extends Annotation> webAppConfigClass = loadWebAppConfigurationClass();
@@ -459,11 +686,12 @@ abstract class ContextLoaderUtils {
Constructor<? extends MergedContextConfiguration> constructor = ClassUtils.getConstructorIfAvailable(
webMergedConfigClass, Class.class, String[].class, Class[].class, Set.class, String[].class,
String.class, ContextLoader.class);
String.class, ContextLoader.class, CacheAwareContextLoaderDelegate.class,
MergedContextConfiguration.class);
if (constructor != null) {
return instantiateClass(constructor, testClass, locations, classes, initializerClasses,
activeProfiles, resourceBasePath, contextLoader);
activeProfiles, resourceBasePath, contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
}
}
catch (Throwable t) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -23,9 +23,11 @@ import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.springframework.context.ApplicationContext;
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;
import org.springframework.util.StringUtils;
@@ -72,6 +74,8 @@ public class MergedContextConfiguration implements Serializable {
private final Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses;
private final String[] activeProfiles;
private final ContextLoader contextLoader;
private final CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate;
private final MergedContextConfiguration parent;
private static String[] processLocations(String[] locations) {
@@ -149,6 +153,7 @@ public class MergedContextConfiguration implements Serializable {
* @param contextInitializerClasses the merged context initializer classes
* @param activeProfiles the merged active bean definition profiles
* @param contextLoader the resolved {@code ContextLoader}
* @see #MergedContextConfiguration(Class, String[], Class[], Set, String[], ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)
*/
public MergedContextConfiguration(
Class<?> testClass,
@@ -156,12 +161,48 @@ public class MergedContextConfiguration implements Serializable {
Class<?>[] classes,
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
String[] activeProfiles, ContextLoader contextLoader) {
this(testClass, locations, classes, contextInitializerClasses, activeProfiles, contextLoader, null, null);
}
/**
* Create a new {@code MergedContextConfiguration} instance for the
* supplied test class, resource locations, annotated classes, context
* initializers, active profiles, {@code ContextLoader}, and parent
* configuration.
*
* <p>If a {@code null} value is supplied for {@code locations},
* {@code classes}, or {@code activeProfiles} an empty array will
* be stored instead. If a {@code null} value is supplied for the
* {@code contextInitializerClasses} 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}
* @param cacheAwareContextLoaderDelegate a cache-aware context loader
* delegate with which to retrieve the parent context
* @param parent the parent configuration or {@code null} if there is no parent
* @since 3.2.2
*/
public MergedContextConfiguration(
Class<?> testClass,
String[] locations,
Class<?>[] classes,
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
String[] activeProfiles, ContextLoader contextLoader,
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parent) {
this.testClass = testClass;
this.locations = processLocations(locations);
this.classes = processClasses(classes);
this.contextInitializerClasses = processContextInitializerClasses(contextInitializerClasses);
this.activeProfiles = processActiveProfiles(activeProfiles);
this.contextLoader = contextLoader;
this.cacheAwareContextLoaderDelegate = cacheAwareContextLoaderDelegate;
this.parent = parent;
}
/**
@@ -207,6 +248,39 @@ public class MergedContextConfiguration implements Serializable {
return contextLoader;
}
/**
* Get the {@link MergedContextConfiguration} for the parent application context in a
* context hierarchy.
*
* @return the parent configuration or {@code null} if there is no parent
* @see #getParentApplicationContext()
* @since 3.2.2
*/
public MergedContextConfiguration getParent() {
return this.parent;
}
/**
* Get the parent {@link ApplicationContext} for the context defined by this
* {@code MergedContextConfiguration} from the context cache.
* <p>
* If the parent context has not yet been loaded, it will be loaded, stored in the
* cache, and then returned.
*
* @return the parent {@code ApplicationContext} or {@code null} if there is no parent
* @see #getParent()
* @since 3.2.2
*/
public ApplicationContext getParentApplicationContext() {
if (parent == null) {
return null;
}
Assert.state(cacheAwareContextLoaderDelegate != null,
"Cannot retrieve a parent application context without access to the CacheAwareContextLoaderDelegate.");
return cacheAwareContextLoaderDelegate.loadContext(parent);
}
/**
* Generate a unique hash code for all properties of this
* {@code MergedContextConfiguration} excluding the
@@ -220,6 +294,7 @@ public class MergedContextConfiguration implements Serializable {
result = prime * result + Arrays.hashCode(classes);
result = prime * result + contextInitializerClasses.hashCode();
result = prime * result + Arrays.hashCode(activeProfiles);
result = prime * result + (parent == null ? 0 : parent.hashCode());
result = prime * result + nullSafeToString(contextLoader).hashCode();
return result;
}
@@ -229,8 +304,9 @@ public class MergedContextConfiguration implements Serializable {
* 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}.
* {@linkplain #getActiveProfiles() active profiles},
* {@linkplain #getParent() parents}, and the fully qualified names of their
* {@link #getContextLoader() ContextLoaders}.
*/
@Override
public boolean equals(Object obj) {
@@ -247,15 +323,28 @@ public class MergedContextConfiguration implements Serializable {
if (!Arrays.equals(this.locations, that.locations)) {
return false;
}
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;
}
if (this.parent == null) {
if (that.parent != null) {
return false;
}
}
else if (!this.parent.equals(that.parent)) {
return false;
}
if (!nullSafeToString(this.contextLoader).equals(nullSafeToString(that.contextLoader))) {
return false;
}
@@ -267,8 +356,9 @@ public class MergedContextConfiguration implements Serializable {
* 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}.
* {@linkplain #getActiveProfiles() active profiles}, the name of the
* {@link #getContextLoader() ContextLoader}, and the
* {@linkplain #getParent() parent configuration}.
*/
@Override
public String toString() {
@@ -279,6 +369,7 @@ public class MergedContextConfiguration implements Serializable {
.append("contextInitializerClasses", ObjectUtils.nullSafeToString(contextInitializerClasses))//
.append("activeProfiles", ObjectUtils.nullSafeToString(activeProfiles))//
.append("contextLoader", nullSafeToString(contextLoader))//
.append("parent", parent)//
.toString();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,8 +29,7 @@ import org.springframework.context.ApplicationContext;
* context that it loads (see {@link MergedContextConfiguration#getActiveProfiles()}
* and {@link #loadContext(MergedContextConfiguration)}).
*
* <p>See the Javadoc for
* {@link ContextConfiguration @ContextConfiguration}
* <p>See the Javadoc for {@link ContextConfiguration @ContextConfiguration}
* for a definition of <em>annotated class</em>.
*
* <p>Clients of a {@code SmartContextLoader} should call
@@ -48,8 +47,8 @@ import org.springframework.context.ApplicationContext;
* <p>Even though {@code SmartContextLoader} extends {@code ContextLoader},
* clients should favor {@code SmartContextLoader}-specific methods over those
* defined in {@code ContextLoader}, particularly because a
* {@code SmartContextLoader} may choose not to support methods defined in
* the {@code ContextLoader} SPI.
* {@code SmartContextLoader} may choose not to support methods defined in the
* {@code ContextLoader} SPI.
*
* <p>Concrete implementations must provide a {@code public} no-args constructor.
*
@@ -59,6 +58,9 @@ import org.springframework.context.ApplicationContext;
* <li>{@link org.springframework.test.context.support.AnnotationConfigContextLoader AnnotationConfigContextLoader}</li>
* <li>{@link org.springframework.test.context.support.GenericXmlContextLoader GenericXmlContextLoader}</li>
* <li>{@link org.springframework.test.context.support.GenericPropertiesContextLoader GenericPropertiesContextLoader}</li>
* <li>{@link org.springframework.test.context.web.WebDelegatingSmartContextLoader WebDelegatingSmartContextLoader}</li>
* <li>{@link org.springframework.test.context.web.AnnotationConfigWebContextLoader AnnotationConfigWebContextLoader}</li>
* <li>{@link org.springframework.test.context.web.GenericXmlWebContextLoader GenericXmlWebContextLoader}</li>
* </ul>
*
* @author Sam Brannen

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -20,9 +20,11 @@ import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.AttributeAccessorSupport;
import org.springframework.core.style.ToStringCreator;
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
import org.springframework.util.Assert;
/**
@@ -41,6 +43,8 @@ public class TestContext extends AttributeAccessorSupport {
private final ContextCache contextCache;
private final CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate;
private final MergedContextConfiguration mergedContextConfiguration;
private final Class<?> testClass;
@@ -61,16 +65,17 @@ public class TestContext extends AttributeAccessorSupport {
}
/**
* Construct a new test context for the supplied {@link Class test class}
* and {@link ContextCache context cache} and parse the corresponding
* {@link ContextConfiguration &#064;ContextConfiguration} annotation, if
* present.
* Construct a new test context for the supplied {@linkplain Class test class}
* and {@linkplain ContextCache context cache} and parse the corresponding
* {@link ContextConfiguration &#064;ContextConfiguration} or
* {@link ContextHierarchy &#064;ContextHierarchy} annotation, if present.
* <p>If the supplied class name for the default {@code ContextLoader}
* is {@code null} or <em>empty</em> and no concrete {@code ContextLoader}
* class is explicitly supplied via the {@code @ContextConfiguration}
* annotation, a
* class is explicitly supplied via {@code @ContextConfiguration}, a
* {@link org.springframework.test.context.support.DelegatingSmartContextLoader
* DelegatingSmartContextLoader} will be used instead.
* DelegatingSmartContextLoader} or
* {@link org.springframework.test.context.web.WebDelegatingSmartContextLoader
* WebDelegatingSmartContextLoader} will be used instead.
* @param testClass the test class for which the test context should be
* constructed (must not be {@code null})
* @param contextCache the context cache from which the constructed test
@@ -83,54 +88,27 @@ public class TestContext extends AttributeAccessorSupport {
Assert.notNull(testClass, "Test class must not be null");
Assert.notNull(contextCache, "ContextCache must not be null");
MergedContextConfiguration mergedContextConfiguration;
ContextConfiguration contextConfiguration = testClass.getAnnotation(ContextConfiguration.class);
this.testClass = testClass;
this.contextCache = contextCache;
this.cacheAwareContextLoaderDelegate = new CacheAwareContextLoaderDelegate(contextCache);
if (contextConfiguration == null) {
MergedContextConfiguration mergedContextConfiguration;
if (testClass.isAnnotationPresent(ContextConfiguration.class)
|| testClass.isAnnotationPresent(ContextHierarchy.class)) {
mergedContextConfiguration = ContextLoaderUtils.buildMergedContextConfiguration(testClass,
defaultContextLoaderClassName, cacheAwareContextLoaderDelegate);
}
else {
if (logger.isInfoEnabled()) {
logger.info(String.format("@ContextConfiguration not found for class [%s]", testClass));
logger.info(String.format(
"Neither @ContextConfiguration nor @ContextHierarchy found for test class [%s]",
testClass.getName()));
}
mergedContextConfiguration = new MergedContextConfiguration(testClass, null, null, null, null);
}
else {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Retrieved @ContextConfiguration [%s] for class [%s]", contextConfiguration,
testClass));
}
mergedContextConfiguration = ContextLoaderUtils.buildMergedContextConfiguration(testClass,
defaultContextLoaderClassName);
}
this.contextCache = contextCache;
this.mergedContextConfiguration = mergedContextConfiguration;
this.testClass = testClass;
}
/**
* Load an {@code ApplicationContext} for this test context using the
* configured {@code ContextLoader} and merged context configuration. Supports
* both the {@link SmartContextLoader} and {@link ContextLoader} SPIs.
* @throws Exception if an error occurs while loading the application context
*/
private ApplicationContext loadApplicationContext() throws Exception {
ContextLoader contextLoader = mergedContextConfiguration.getContextLoader();
Assert.notNull(contextLoader, "Cannot load an ApplicationContext with a NULL 'contextLoader'. "
+ "Consider annotating your test class with @ContextConfiguration.");
ApplicationContext applicationContext;
if (contextLoader instanceof SmartContextLoader) {
SmartContextLoader smartContextLoader = (SmartContextLoader) contextLoader;
applicationContext = smartContextLoader.loadContext(mergedContextConfiguration);
}
else {
String[] locations = mergedContextConfiguration.getLocations();
Assert.notNull(locations, "Cannot load an ApplicationContext with a NULL 'locations' array. "
+ "Consider annotating your test class with @ContextConfiguration.");
applicationContext = contextLoader.loadContext(locations);
}
return applicationContext;
}
/**
@@ -141,31 +119,7 @@ public class TestContext extends AttributeAccessorSupport {
* application context
*/
public ApplicationContext getApplicationContext() {
synchronized (contextCache) {
ApplicationContext context = contextCache.get(mergedContextConfiguration);
if (context == null) {
try {
context = loadApplicationContext();
if (logger.isDebugEnabled()) {
logger.debug(String.format(
"Storing ApplicationContext for test class [%s] in cache under key [%s].", testClass,
mergedContextConfiguration));
}
contextCache.put(mergedContextConfiguration, context);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to load ApplicationContext", ex);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug(String.format(
"Retrieved ApplicationContext for test class [%s] from cache with key [%s].", testClass,
mergedContextConfiguration));
}
}
return context;
}
return cacheAwareContextLoaderDelegate.loadContext(mergedContextConfiguration);
}
/**
@@ -209,15 +163,27 @@ public class TestContext extends AttributeAccessorSupport {
}
/**
* Call this method to signal that the {@link ApplicationContext application
* context} associated with this test context is <em>dirty</em> and should
* be reloaded. Do this if a test has modified the context (for example, by
* replacing a bean definition).
* Call this method to signal that the {@linkplain ApplicationContext application
* context} associated with this test context is <em>dirty</em> and should be
* discarded. Do this if a test has modified the context &mdash; for example,
* by replacing a bean definition or modifying the state of a singleton bean.
* @deprecated As of Spring 3.2.2, use {@link #markApplicationContextDirty(HierarchyMode)} instead.
*/
@Deprecated
public void markApplicationContextDirty() {
synchronized (contextCache) {
contextCache.setDirty(mergedContextConfiguration);
}
markApplicationContextDirty((HierarchyMode) null);
}
/**
* Call this method to signal that the {@linkplain ApplicationContext application
* context} associated with this test context is <em>dirty</em> and should be
* discarded. Do this if a test has modified the context &mdash; for example,
* by replacing a bean definition or modifying the state of a singleton bean.
* @param hierarchyMode the context cache clearing mode to be applied if the
* context is part of a hierarchy (may be {@code null})
*/
public void markApplicationContextDirty(HierarchyMode hierarchyMode) {
contextCache.remove(mergedContextConfiguration, hierarchyMode);
}
/**

View File

@@ -104,15 +104,14 @@ public class TestContextManager {
}
/**
* Constructs a new {@code TestContextManager} for the specified {@link Class test class}
* and automatically {@link #registerTestExecutionListeners registers} the
* Constructs a new {@code TestContextManager} for the specified {@linkplain Class
* test class} and automatically {@link #registerTestExecutionListeners registers} the
* {@link TestExecutionListener TestExecutionListeners} configured for the test class
* via the {@link TestExecutionListeners &#064;TestExecutionListeners} annotation.
* @param testClass the test class to be managed
* @param defaultContextLoaderClassName the name of the default
* {@code ContextLoader} class to use (may be {@code null})
* @param defaultContextLoaderClassName the name of the default {@code ContextLoader}
* class to use (may be {@code null})
* @see #registerTestExecutionListeners(TestExecutionListener...)
* @see #retrieveTestExecutionListeners(Class)
*/
public TestContextManager(Class<?> testClass, String defaultContextLoaderClassName) {
this.testContext = new TestContext(testClass, contextCache, defaultContextLoaderClassName);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -17,29 +17,24 @@
package org.springframework.test.context;
/**
* {@code TestExecutionListener} defines a <em>listener</em> API for reacting to
* test execution events published by the {@link TestContextManager} with which
* the listener is registered.
* <p>
* {@code TestExecutionListener} defines a <em>listener</em> API for
* reacting to test execution events published by the {@link TestContextManager}
* with which the listener is registered.
* </p>
* <p>
* Concrete implementations must provide a {@code public} no-args
* constructor, so that listeners can be instantiated transparently by tools and
* configuration mechanisms.
* </p>
* Concrete implementations must provide a {@code public} no-args constructor,
* so that listeners can be instantiated transparently by tools and configuration
* mechanisms.
* <p>
* Spring provides the following out-of-the-box implementations:
* </p>
* <ul>
* <li>
* {@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener
* <li>{@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener
* DependencyInjectionTestExecutionListener}</li>
* <li>
* {@link org.springframework.test.context.support.DirtiesContextTestExecutionListener
* <li>{@link org.springframework.test.context.support.DirtiesContextTestExecutionListener
* DirtiesContextTestExecutionListener}</li>
* <li>
* {@link org.springframework.test.context.transaction.TransactionalTestExecutionListener
* <li>{@link org.springframework.test.context.transaction.TransactionalTestExecutionListener
* TransactionalTestExecutionListener}</li>
* <li>{@link org.springframework.test.context.web.ServletTestExecutionListener
* ServletTestExecutionListener}</li>
* </ul>
*
* @author Sam Brannen

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -26,9 +26,10 @@ import java.lang.annotation.Target;
/**
* {@code TestExecutionListeners} defines class-level metadata for
* configuring which {@link TestExecutionListener TestExecutionListeners} should
* be registered with a {@link TestContextManager}. Typically,
* {@code &#064;TestExecutionListeners} will be used in conjunction with
* {@link ContextConfiguration &#064;ContextConfiguration}.
* be registered with a {@link TestContextManager}.
*
* <p>Typically, {@code @TestExecutionListeners} will be used in conjunction with
* {@link ContextConfiguration @ContextConfiguration}.
*
* @author Sam Brannen
* @since 2.5
@@ -43,11 +44,10 @@ import java.lang.annotation.Target;
public @interface TestExecutionListeners {
/**
* <p>
* The {@link TestExecutionListener TestExecutionListeners} to register with
* a {@link TestContextManager}.
* </p>
*
* @see org.springframework.test.context.web.ServletTestExecutionListener
* @see org.springframework.test.context.support.DependencyInjectionTestExecutionListener
* @see org.springframework.test.context.support.DirtiesContextTestExecutionListener
* @see org.springframework.test.context.transaction.TransactionalTestExecutionListener
@@ -60,10 +60,8 @@ public @interface TestExecutionListeners {
Class<? extends TestExecutionListener>[] value() default {};
/**
* <p>
* Whether or not {@link #value() TestExecutionListeners} from superclasses
* should be <em>inherited</em>.
* </p>
* <p>
* The default value is {@code true}, which means that an annotated
* class will <em>inherit</em> the listeners defined by an annotated
@@ -77,11 +75,12 @@ public @interface TestExecutionListeners {
* {@code DependencyInjectionTestExecutionListener},
* {@code DirtiesContextTestExecutionListener}, <strong>and</strong>
* {@code TransactionalTestExecutionListener}, in that order.
* </p>
*
* <pre class="code">
* &#064;TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
* DirtiesContextTestExecutionListener.class })
* &#064;TestExecutionListeners({
* DependencyInjectionTestExecutionListener.class,
* DirtiesContextTestExecutionListener.class
* })
* public abstract class AbstractBaseTest {
* // ...
* }
@@ -89,14 +88,12 @@ public @interface TestExecutionListeners {
* &#064;TestExecutionListeners(TransactionalTestExecutionListener.class)
* public class TransactionalTest extends AbstractBaseTest {
* // ...
* }
* </pre>
* }</pre>
*
* <p>
* If {@code inheritListeners} is set to {@code false}, the
* listeners for the annotated class will <em>shadow</em> and effectively
* replace any listeners defined by a superclass.
* </p>
* If {@code inheritListeners} is set to {@code false}, the listeners for the
* annotated class will <em>shadow</em> and effectively replace any listeners
* defined by a superclass.
*/
boolean inheritListeners() default true;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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,10 +16,13 @@
package org.springframework.test.context.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.support.GenericApplicationContext;
@@ -66,6 +69,12 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
*
* <ul>
* <li>Creates a {@link GenericApplicationContext} instance.</li>
* <li>If the supplied {@code MergedContextConfiguration} references a
* {@linkplain MergedContextConfiguration#getParent() parent configuration},
* the corresponding {@link MergedContextConfiguration#getParentApplicationContext()
* ApplicationContext} will be retrieved and
* {@linkplain GenericApplicationContext#setParent(ApplicationContext) set as the parent}
* for the context created by this method.</li>
* <li>Calls {@link #prepareContext(GenericApplicationContext)} for backwards
* compatibility with the {@link org.springframework.test.context.ContextLoader
* ContextLoader} SPI.</li>
@@ -97,6 +106,11 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
}
GenericApplicationContext context = new GenericApplicationContext();
ApplicationContext parent = mergedConfig.getParentApplicationContext();
if (parent != null) {
context.setParent(parent);
}
prepareContext(context);
prepareContext(context, mergedConfig);
customizeBeanFactory(context.getDefaultListableBeanFactory());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -23,6 +23,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
import org.springframework.test.context.TestContext;
import org.springframework.util.Assert;
@@ -43,26 +44,47 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi
/**
* Marks the {@link ApplicationContext application context} of the supplied
* {@link TestContext test context} as
* {@link TestContext#markApplicationContextDirty() dirty}, and sets the
* Marks the {@linkplain ApplicationContext application context} of the supplied
* {@linkplain TestContext test context} as
* {@linkplain TestContext#markApplicationContextDirty() dirty}, and sets the
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE
* REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context to {@code true}.
* @param testContext the test context whose application context should
* marked as dirty
* @deprecated as of Spring 3.2.2, use {@link #dirtyContext(TestContext, HierarchyMode)} instead.
*/
@Deprecated
protected void dirtyContext(TestContext testContext) {
testContext.markApplicationContextDirty();
testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE, Boolean.TRUE);
}
/**
* If the current test method of the supplied {@link TestContext test
* Marks the {@linkplain ApplicationContext application context} of the supplied
* {@linkplain TestContext test context} as {@linkplain
* TestContext#markApplicationContextDirty(HierarchyMode) dirty} and sets the
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE
* REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context to {@code true}.
* @param testContext the test context whose application context should
* marked as dirty
* @param hierarchyMode the context cache clearing mode to be applied if the
* context is part of a hierarchy; may be {@code null}
* @since 3.2.2
*/
protected void dirtyContext(TestContext testContext, HierarchyMode hierarchyMode) {
testContext.markApplicationContextDirty(hierarchyMode);
testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE, Boolean.TRUE);
}
/**
* If the current test method of the supplied {@linkplain TestContext test
* context} is annotated with {@link DirtiesContext &#064;DirtiesContext},
* or if the test class is annotated with {@link DirtiesContext
* &#064;DirtiesContext} and the {@link DirtiesContext#classMode() class
* &#064;DirtiesContext} and the {@linkplain DirtiesContext#classMode() class
* mode} is set to {@link ClassMode#AFTER_EACH_TEST_METHOD
* AFTER_EACH_TEST_METHOD}, the {@link ApplicationContext application
* AFTER_EACH_TEST_METHOD}, the {@linkplain ApplicationContext application
* context} of the test context will be
* {@link TestContext#markApplicationContextDirty() marked as dirty} and the
* {@linkplain TestContext#markApplicationContextDirty() marked as dirty} and the
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE
* REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context will be set to
* {@code true}.
@@ -88,15 +110,17 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi
}
if (methodDirtiesContext || (classDirtiesContext && classMode == ClassMode.AFTER_EACH_TEST_METHOD)) {
dirtyContext(testContext);
HierarchyMode hierarchyMode = methodDirtiesContext ? testMethod.getAnnotation(annotationType).hierarchyMode()
: classDirtiesContextAnnotation.hierarchyMode();
dirtyContext(testContext, hierarchyMode);
}
}
/**
* If the test class of the supplied {@link TestContext test context} is
* If the test class of the supplied {@linkplain TestContext test context} is
* annotated with {@link DirtiesContext &#064;DirtiesContext}, the
* {@link ApplicationContext application context} of the test context will
* be {@link TestContext#markApplicationContextDirty() marked as dirty} ,
* {@linkplain ApplicationContext application context} of the test context will
* be {@linkplain TestContext#markApplicationContextDirty() marked as dirty} ,
* and the
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE
* REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context will be set to
@@ -107,12 +131,15 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi
Class<?> testClass = testContext.getTestClass();
Assert.notNull(testClass, "The test class of the supplied TestContext must not be null");
boolean dirtiesContext = testClass.isAnnotationPresent(DirtiesContext.class);
final Class<DirtiesContext> annotationType = DirtiesContext.class;
boolean dirtiesContext = testClass.isAnnotationPresent(annotationType);
if (logger.isDebugEnabled()) {
logger.debug("After test class: context [" + testContext + "], dirtiesContext [" + dirtiesContext + "].");
}
if (dirtiesContext) {
dirtyContext(testContext);
HierarchyMode hierarchyMode = testClass.getAnnotation(annotationType).hierarchyMode();
dirtyContext(testContext, hierarchyMode);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -31,6 +31,7 @@ import org.springframework.core.io.ResourceLoader;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.support.AbstractContextLoader;
import org.springframework.util.Assert;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.GenericWebApplicationContext;
@@ -71,6 +72,12 @@ public abstract class AbstractGenericWebContextLoader extends AbstractContextLoa
*
* <ul>
* <li>Creates a {@link GenericWebApplicationContext} instance.</li>
* <li>If the supplied {@code MergedContextConfiguration} references a
* {@linkplain MergedContextConfiguration#getParent() parent configuration},
* the corresponding {@link MergedContextConfiguration#getParentApplicationContext()
* ApplicationContext} will be retrieved and
* {@linkplain GenericWebApplicationContext#setParent(ApplicationContext) set as the parent}
* for the context created by this method.</li>
* <li>Delegates to {@link #configureWebResources} to create the
* {@link MockServletContext} and set it in the {@code WebApplicationContext}.</li>
* <li>Calls {@link #prepareContext} to allow for customizing the context
@@ -107,6 +114,11 @@ public abstract class AbstractGenericWebContextLoader extends AbstractContextLoa
}
GenericWebApplicationContext context = new GenericWebApplicationContext();
ApplicationContext parent = mergedConfig.getParentApplicationContext();
if (parent != null) {
context.setParent(parent);
}
configureWebResources(context, webMergedConfig);
prepareContext(context, webMergedConfig);
customizeBeanFactory(context.getDefaultListableBeanFactory(), webMergedConfig);
@@ -119,9 +131,20 @@ public abstract class AbstractGenericWebContextLoader extends AbstractContextLoa
}
/**
* Configures web resources for the supplied web application context.
* Configures web resources for the supplied web application context (WAC).
*
* <p>Implementation details:
* <h4>Implementation Details</h4>
*
* <p>If the supplied WAC has no parent or its parent is not a WAC, the
* supplied WAC will be configured as the Root WAC (see "<em>Root WAC
* Configuration</em>" below).
*
* <p>Otherwise the context hierarchy of the supplied WAC will be traversed
* to find the top-most WAC (i.e., the root); and the {@link ServletContext}
* of the Root WAC will be set as the {@code ServletContext} for the supplied
* WAC.
*
* <h4>Root WAC Configuration</h4>
*
* <ul>
* <li>The resource base path is retrieved from the supplied
@@ -146,13 +169,33 @@ public abstract class AbstractGenericWebContextLoader extends AbstractContextLoa
protected void configureWebResources(GenericWebApplicationContext context,
WebMergedContextConfiguration webMergedConfig) {
String resourceBasePath = webMergedConfig.getResourceBasePath();
ResourceLoader resourceLoader = resourceBasePath.startsWith(ResourceLoader.CLASSPATH_URL_PREFIX) ? new DefaultResourceLoader()
: new FileSystemResourceLoader();
ApplicationContext parent = context.getParent();
ServletContext servletContext = new MockServletContext(resourceBasePath, resourceLoader);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
context.setServletContext(servletContext);
// if the WAC has no parent or the parent is not a WAC, set the WAC as
// the Root WAC:
if (parent == null || (!(parent instanceof WebApplicationContext))) {
String resourceBasePath = webMergedConfig.getResourceBasePath();
ResourceLoader resourceLoader = resourceBasePath.startsWith(ResourceLoader.CLASSPATH_URL_PREFIX) ? new DefaultResourceLoader()
: new FileSystemResourceLoader();
ServletContext servletContext = new MockServletContext(resourceBasePath, resourceLoader);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
context.setServletContext(servletContext);
}
else {
ServletContext servletContext = null;
// find the Root WAC
while (parent != null) {
if (parent instanceof WebApplicationContext && !(parent.getParent() instanceof WebApplicationContext)) {
servletContext = ((WebApplicationContext) parent).getServletContext();
break;
}
parent = parent.getParent();
}
Assert.state(servletContext != null, "Failed to find Root WebApplicationContext in the context hierarchy");
context.setServletContext(servletContext);
}
}
/**

View File

@@ -69,6 +69,7 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener
* @see TestExecutionListener#prepareTestInstance(TestContext)
* @see #setUpRequestContextIfNecessary(TestContext)
*/
@SuppressWarnings("javadoc")
public void prepareTestInstance(TestContext testContext) throws Exception {
setUpRequestContextIfNecessary(testContext);
}
@@ -80,6 +81,7 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener
* @see TestExecutionListener#beforeTestMethod(TestContext)
* @see #setUpRequestContextIfNecessary(TestContext)
*/
@SuppressWarnings("javadoc")
public void beforeTestMethod(TestContext testContext) throws Exception {
setUpRequestContextIfNecessary(testContext);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import java.util.Set;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.style.ToStringCreator;
import org.springframework.test.context.CacheAwareContextLoaderDelegate;
import org.springframework.test.context.ContextLoader;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.util.ObjectUtils;
@@ -77,7 +78,11 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
* @param activeProfiles the merged active bean definition profiles
* @param resourceBasePath the resource path to the root directory of the web application
* @param contextLoader the resolved {@code ContextLoader}
* @see #WebMergedContextConfiguration(Class, String[], Class[], Set, String[], String, ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)
* @deprecated as of Spring 3.2.2, use
* {@link #WebMergedContextConfiguration(Class, String[], Class[], Set, String[], String, ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)} instead.
*/
@Deprecated
public WebMergedContextConfiguration(
Class<?> testClass,
String[] locations,
@@ -85,7 +90,45 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
String[] activeProfiles, String resourceBasePath, ContextLoader contextLoader) {
super(testClass, locations, classes, contextInitializerClasses, activeProfiles, contextLoader);
this(testClass, locations, classes, contextInitializerClasses, activeProfiles, resourceBasePath, contextLoader,
null, null);
}
/**
* Create a new {@code WebMergedContextConfiguration} instance for the
* supplied test class, resource locations, annotated classes, context
* initializers, active profiles, resource base path, and {@code ContextLoader}.
*
* <p>If a {@code null} value is supplied for {@code locations},
* {@code classes}, or {@code activeProfiles} an empty array will
* be stored instead. If a {@code null} value is supplied for the
* {@code contextInitializerClasses} an empty set will be stored instead.
* If an <em>empty</em> value is supplied for the {@code resourceBasePath}
* an empty string will be used. 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 resourceBasePath the resource path to the root directory of the web application
* @param contextLoader the resolved {@code ContextLoader}
* @param cacheAwareContextLoaderDelegate a cache-aware context loader
* delegate with which to retrieve the parent context
* @param parent the parent configuration or {@code null} if there is no parent
* @since 3.2.2
*/
public WebMergedContextConfiguration(
Class<?> testClass,
String[] locations,
Class<?>[] classes,
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
String[] activeProfiles, String resourceBasePath, ContextLoader contextLoader,
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parent) {
super(testClass, locations, classes, contextInitializerClasses, activeProfiles, contextLoader,
cacheAwareContextLoaderDelegate, parent);
this.resourceBasePath = !StringUtils.hasText(resourceBasePath) ? "" : resourceBasePath;
}
@@ -118,8 +161,9 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
* {@linkplain #getClasses() annotated classes},
* {@linkplain #getContextInitializerClasses() context initializer classes},
* {@linkplain #getActiveProfiles() active profiles},
* {@linkplain #getResourceBasePath() resource base path}, and the fully
* qualified names of their {@link #getContextLoader() ContextLoaders}.
* {@linkplain #getResourceBasePath() resource base path},
* {@linkplain #getParent() parents}, and the fully qualified names of their
* {@link #getContextLoader() ContextLoaders}.
*/
@Override
public boolean equals(Object obj) {
@@ -141,8 +185,9 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
* {@linkplain #getLocations() locations}, {@linkplain #getClasses() annotated classes},
* {@linkplain #getContextInitializerClasses() context initializer classes},
* {@linkplain #getActiveProfiles() active profiles},
* {@linkplain #getResourceBasePath() resource base path}, and the name of the
* {@link #getContextLoader() ContextLoader}.
* {@linkplain #getResourceBasePath() resource base path}, the name of the
* {@link #getContextLoader() ContextLoader}, and the
* {@linkplain #getParent() parent configuration}.
*/
@Override
public String toString() {
@@ -154,6 +199,7 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
.append("activeProfiles", ObjectUtils.nullSafeToString(getActiveProfiles()))//
.append("resourceBasePath", getResourceBasePath())//
.append("contextLoader", nullSafeToString(getContextLoader()))//
.append("parent", getParent())//
.toString();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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
@@ -12,10 +12,6 @@
*/
package org.springframework.mock.web;
import static org.easymock.EasyMock.*;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.*;
import java.io.IOException;
import javax.servlet.Filter;
@@ -29,6 +25,10 @@ import javax.servlet.ServletResponse;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
/**
* Test fixture for {@link MockFilterChain}.
*
@@ -53,7 +53,7 @@ public class MockFilterChainTests {
@Test(expected=IllegalArgumentException.class)
public void constructorNullFilter() {
new MockFilterChain(createMock(Servlet.class), (Filter) null);
new MockFilterChain(mock(Servlet.class), (Filter) null);
}
@Test(expected = IllegalArgumentException.class)
@@ -87,16 +87,10 @@ public class MockFilterChainTests {
@Test
public void doFilterWithServlet() throws Exception {
Servlet servlet = createMock(Servlet.class);
Servlet servlet = mock(Servlet.class);
MockFilterChain chain = new MockFilterChain(servlet);
servlet.service(this.request, this.response);
replay(servlet);
chain.doFilter(this.request, this.response);
verify(servlet);
verify(servlet).service(this.request, this.response);
try {
chain.doFilter(this.request, this.response);
fail("Expected Exception");
@@ -108,9 +102,7 @@ public class MockFilterChainTests {
@Test
public void doFilterWithServletAndFilters() throws Exception {
Servlet servlet = createMock(Servlet.class);
servlet.service(this.request, this.response);
replay(servlet);
Servlet servlet = mock(Servlet.class);
MockFilter filter2 = new MockFilter(servlet);
MockFilter filter1 = new MockFilter(null);
@@ -121,7 +113,7 @@ public class MockFilterChainTests {
assertTrue(filter1.invoked);
assertTrue(filter2.invoked);
verify(servlet);
verify(servlet).service(this.request, this.response);
try {
chain.doFilter(this.request, this.response);

View File

@@ -0,0 +1,329 @@
/*
* Copyright 2002-2013 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;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.test.util.ReflectionTestUtils;
import static org.junit.Assert.*;
import static org.springframework.test.context.SpringRunnerContextCacheTests.*;
/**
* Integration tests for verifying proper behavior of the {@link ContextCache} in
* conjunction with cache keys used in {@link TestContext}.
*
* @author Sam Brannen
* @since 3.1
* @see SpringRunnerContextCacheTests
*/
public class ContextCacheTests {
private ContextCache contextCache = new ContextCache();
@Before
public void initialCacheState() {
assertContextCacheStatistics(contextCache, "initial state", 0, 0, 0);
assertParentContextCount(0);
}
private void assertParentContextCount(int expected) {
assertEquals("parent context count", expected, contextCache.getParentContextCount());
}
private MergedContextConfiguration getMergedContextConfiguration(TestContext testContext) {
return (MergedContextConfiguration) ReflectionTestUtils.getField(testContext, "mergedContextConfiguration");
}
private ApplicationContext loadContext(Class<?> testClass) {
TestContext testContext = new TestContext(testClass, contextCache);
return testContext.getApplicationContext();
}
private void loadCtxAndAssertStats(Class<?> testClass, int expectedSize, int expectedHitCount, int expectedMissCount) {
assertNotNull(loadContext(testClass));
assertContextCacheStatistics(contextCache, testClass.getName(), expectedSize, expectedHitCount,
expectedMissCount);
}
@Test
public void verifyCacheKeyIsBasedOnContextLoader() {
loadCtxAndAssertStats(AnnotationConfigContextLoaderTestCase.class, 1, 0, 1);
loadCtxAndAssertStats(AnnotationConfigContextLoaderTestCase.class, 1, 1, 1);
loadCtxAndAssertStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 1, 2);
loadCtxAndAssertStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 2, 2);
loadCtxAndAssertStats(AnnotationConfigContextLoaderTestCase.class, 2, 3, 2);
loadCtxAndAssertStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 4, 2);
}
@Test
public void verifyCacheKeyIsBasedOnActiveProfiles() {
loadCtxAndAssertStats(FooBarProfilesTestCase.class, 1, 0, 1);
loadCtxAndAssertStats(FooBarProfilesTestCase.class, 1, 1, 1);
// Profiles {foo, bar} should hash to the same as {bar,foo}
loadCtxAndAssertStats(BarFooProfilesTestCase.class, 1, 2, 1);
loadCtxAndAssertStats(FooBarProfilesTestCase.class, 1, 3, 1);
loadCtxAndAssertStats(FooBarProfilesTestCase.class, 1, 4, 1);
loadCtxAndAssertStats(BarFooProfilesTestCase.class, 1, 5, 1);
}
@Test
public void verifyCacheBehaviorForContextHierarchies() {
int size = 0;
int hits = 0;
int misses = 0;
// Level 1
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel1TestCase.class, ++size, hits, ++misses);
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel1TestCase.class, size, ++hits, misses);
// Level 2
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel2TestCase.class, ++size /* L2 */, ++hits /* L1 */,
++misses /* L2 */);
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel2TestCase.class, size, ++hits /* L2 */, misses);
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel2TestCase.class, size, ++hits /* L2 */, misses);
// Level 3-A
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel3aTestCase.class, ++size /* L3A */, ++hits /* L2 */,
++misses /* L3A */);
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel3aTestCase.class, size, ++hits /* L3A */, misses);
// Level 3-B
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel3bTestCase.class, ++size /* L3B */, ++hits /* L2 */,
++misses /* L3B */);
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel3bTestCase.class, size, ++hits /* L3B */, misses);
}
@Test
public void removeContextHierarchyCacheLevel1() {
// Load Level 3-A
TestContext testContext3a = new TestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
testContext3a.getApplicationContext();
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
assertParentContextCount(2);
// Load Level 3-B
TestContext testContext3b = new TestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
testContext3b.getApplicationContext();
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
assertParentContextCount(2);
// Remove Level 1
// Should also remove Levels 2, 3-A, and 3-B, leaving nothing.
contextCache.remove(getMergedContextConfiguration(testContext3a).getParent().getParent(),
HierarchyMode.CURRENT_LEVEL);
assertContextCacheStatistics(contextCache, "removed level 1", 0, 1, 4);
assertParentContextCount(0);
}
@Test
public void removeContextHierarchyCacheLevel1WithExhaustiveMode() {
// Load Level 3-A
TestContext testContext3a = new TestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
testContext3a.getApplicationContext();
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
assertParentContextCount(2);
// Load Level 3-B
TestContext testContext3b = new TestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
testContext3b.getApplicationContext();
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
assertParentContextCount(2);
// Remove Level 1
// Should also remove Levels 2, 3-A, and 3-B, leaving nothing.
contextCache.remove(getMergedContextConfiguration(testContext3a).getParent().getParent(),
HierarchyMode.EXHAUSTIVE);
assertContextCacheStatistics(contextCache, "removed level 1", 0, 1, 4);
assertParentContextCount(0);
}
@Test
public void removeContextHierarchyCacheLevel2() {
// Load Level 3-A
TestContext testContext3a = new TestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
testContext3a.getApplicationContext();
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
assertParentContextCount(2);
// Load Level 3-B
TestContext testContext3b = new TestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
testContext3b.getApplicationContext();
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
assertParentContextCount(2);
// Remove Level 2
// Should also remove Levels 3-A and 3-B, leaving only Level 1 as a context in the
// cache but also removing the Level 1 hierarchy since all children have been
// removed.
contextCache.remove(getMergedContextConfiguration(testContext3a).getParent(), HierarchyMode.CURRENT_LEVEL);
assertContextCacheStatistics(contextCache, "removed level 2", 1, 1, 4);
assertParentContextCount(0);
}
@Test
public void removeContextHierarchyCacheLevel2WithExhaustiveMode() {
// Load Level 3-A
TestContext testContext3a = new TestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
testContext3a.getApplicationContext();
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
assertParentContextCount(2);
// Load Level 3-B
TestContext testContext3b = new TestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
testContext3b.getApplicationContext();
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
assertParentContextCount(2);
// Remove Level 2
// Should wipe the cache
contextCache.remove(getMergedContextConfiguration(testContext3a).getParent(), HierarchyMode.EXHAUSTIVE);
assertContextCacheStatistics(contextCache, "removed level 2", 0, 1, 4);
assertParentContextCount(0);
}
@Test
public void removeContextHierarchyCacheLevel3Then2() {
// Load Level 3-A
TestContext testContext3a = new TestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
testContext3a.getApplicationContext();
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
assertParentContextCount(2);
// Load Level 3-B
TestContext testContext3b = new TestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
testContext3b.getApplicationContext();
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
assertParentContextCount(2);
// Remove Level 3-A
contextCache.remove(getMergedContextConfiguration(testContext3a), HierarchyMode.CURRENT_LEVEL);
assertContextCacheStatistics(contextCache, "removed level 3-A", 3, 1, 4);
assertParentContextCount(2);
// Remove Level 2
// Should also remove Level 3-B, leaving only Level 1.
contextCache.remove(getMergedContextConfiguration(testContext3b).getParent(), HierarchyMode.CURRENT_LEVEL);
assertContextCacheStatistics(contextCache, "removed level 2", 1, 1, 4);
assertParentContextCount(0);
}
@Test
public void removeContextHierarchyCacheLevel3Then2WithExhaustiveMode() {
// Load Level 3-A
TestContext testContext3a = new TestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
testContext3a.getApplicationContext();
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
assertParentContextCount(2);
// Load Level 3-B
TestContext testContext3b = new TestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
testContext3b.getApplicationContext();
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
assertParentContextCount(2);
// Remove Level 3-A
// Should wipe the cache.
contextCache.remove(getMergedContextConfiguration(testContext3a), HierarchyMode.EXHAUSTIVE);
assertContextCacheStatistics(contextCache, "removed level 3-A", 0, 1, 4);
assertParentContextCount(0);
// Remove Level 2
// Should not actually do anything since the cache was cleared in the
// previous step. So the stats should remain the same.
contextCache.remove(getMergedContextConfiguration(testContext3b).getParent(), HierarchyMode.EXHAUSTIVE);
assertContextCacheStatistics(contextCache, "removed level 2", 0, 1, 4);
assertParentContextCount(0);
}
@Configuration
static class Config {
}
@ContextConfiguration(classes = Config.class, loader = AnnotationConfigContextLoader.class)
private static class AnnotationConfigContextLoaderTestCase {
}
@ContextConfiguration(classes = Config.class, loader = CustomAnnotationConfigContextLoader.class)
private static class CustomAnnotationConfigContextLoaderTestCase {
}
private static class CustomAnnotationConfigContextLoader extends AnnotationConfigContextLoader {
}
@ActiveProfiles({ "foo", "bar" })
@ContextConfiguration(classes = Config.class, loader = AnnotationConfigContextLoader.class)
private static class FooBarProfilesTestCase {
}
@ActiveProfiles({ "bar", "foo" })
@ContextConfiguration(classes = Config.class, loader = AnnotationConfigContextLoader.class)
private static class BarFooProfilesTestCase {
}
@ContextHierarchy({ @ContextConfiguration })
private static class ClassHierarchyContextHierarchyLevel1TestCase {
@Configuration
static class Level1Config {
}
}
@ContextHierarchy({ @ContextConfiguration })
private static class ClassHierarchyContextHierarchyLevel2TestCase extends
ClassHierarchyContextHierarchyLevel1TestCase {
@Configuration
static class Level2Config {
}
}
@ContextHierarchy({ @ContextConfiguration })
private static class ClassHierarchyContextHierarchyLevel3aTestCase extends
ClassHierarchyContextHierarchyLevel2TestCase {
@Configuration
static class Level3aConfig {
}
}
@ContextHierarchy({ @ContextConfiguration })
private static class ClassHierarchyContextHierarchyLevel3bTestCase extends
ClassHierarchyContextHierarchyLevel2TestCase {
@Configuration
static class Level3bConfig {
}
}
}

View File

@@ -0,0 +1,212 @@
/*
* Copyright 2002-2013 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;
import org.junit.After;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
/**
* Integration tests that verify proper behavior of {@link DirtiesContext @DirtiesContext}
* in conjunction with context hierarchies configured via {@link ContextHierarchy @ContextHierarchy}.
*
* @author Sam Brannen
* @author Tadaya Tsuyukubo
* @since 3.2.2
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ContextHierarchyDirtiesContextTests {
private static ApplicationContext context;
@After
public void cleanUp() {
ContextHierarchyDirtiesContextTests.context = null;
}
@Test
public void classLevelDirtiesContextWithCurrentLevelHierarchyMode() {
runTestAndVerifyHierarchies(ClassLevelDirtiesContextWithCurrentLevelModeTestCase.class, true, true, false);
}
@Test
public void classLevelDirtiesContextWithExhaustiveHierarchyMode() {
runTestAndVerifyHierarchies(ClassLevelDirtiesContextWithExhaustiveModeTestCase.class, false, false, false);
}
@Test
public void methodLevelDirtiesContextWithCurrentLevelHierarchyMode() {
runTestAndVerifyHierarchies(MethodLevelDirtiesContextWithCurrentLevelModeTestCase.class, true, true, false);
}
@Test
public void methodLevelDirtiesContextWithExhaustiveHierarchyMode() {
runTestAndVerifyHierarchies(MethodLevelDirtiesContextWithExhaustiveModeTestCase.class, false, false, false);
}
private void runTestAndVerifyHierarchies(Class<? extends FooTestCase> testClass, boolean isFooContextActive,
boolean isBarContextActive, boolean isBazContextActive) {
JUnitCore jUnitCore = new JUnitCore();
Result result = jUnitCore.run(testClass);
assertTrue("all tests passed", result.wasSuccessful());
assertThat(ContextHierarchyDirtiesContextTests.context, notNullValue());
ConfigurableApplicationContext bazContext = (ConfigurableApplicationContext) ContextHierarchyDirtiesContextTests.context;
assertEquals("baz", bazContext.getBean("bean", String.class));
assertThat("bazContext#isActive()", bazContext.isActive(), is(isBazContextActive));
ConfigurableApplicationContext barContext = (ConfigurableApplicationContext) bazContext.getParent();
assertThat(barContext, notNullValue());
assertEquals("bar", barContext.getBean("bean", String.class));
assertThat("barContext#isActive()", barContext.isActive(), is(isBarContextActive));
ConfigurableApplicationContext fooContext = (ConfigurableApplicationContext) barContext.getParent();
assertThat(fooContext, notNullValue());
assertEquals("foo", fooContext.getBean("bean", String.class));
assertThat("fooContext#isActive()", fooContext.isActive(), is(isFooContextActive));
}
// -------------------------------------------------------------------------
@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy(@ContextConfiguration(name = "foo"))
static abstract class FooTestCase implements ApplicationContextAware {
@Configuration
static class Config {
@Bean
public String bean() {
return "foo";
}
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ContextHierarchyDirtiesContextTests.context = applicationContext;
}
}
@ContextHierarchy(@ContextConfiguration(name = "bar"))
static abstract class BarTestCase extends FooTestCase {
@Configuration
static class Config {
@Bean
public String bean() {
return "bar";
}
}
}
@ContextHierarchy(@ContextConfiguration(name = "baz"))
static abstract class BazTestCase extends BarTestCase {
@Configuration
static class Config {
@Bean
public String bean() {
return "baz";
}
}
}
// -------------------------------------------------------------------------
/**
* {@link DirtiesContext} is declared at the class level, without specifying
* the {@link DirtiesContext.HierarchyMode}.
* <p>After running this test class, the context cache should be <em>exhaustively</em>
* cleared beginning from the current context hierarchy, upwards to the highest
* parent context, and then back down through all subhierarchies of the parent
* context.
*/
@DirtiesContext
public static class ClassLevelDirtiesContextWithExhaustiveModeTestCase extends BazTestCase {
@Test
public void test() {
}
}
/**
* {@link DirtiesContext} is declared at the class level, specifying the
* {@link DirtiesContext.HierarchyMode#CURRENT_LEVEL CURRENT_LEVEL} hierarchy mode.
* <p>After running this test class, the context cache should be cleared
* beginning from the current context hierarchy and down through all subhierarchies.
*/
@DirtiesContext(hierarchyMode = HierarchyMode.CURRENT_LEVEL)
public static class ClassLevelDirtiesContextWithCurrentLevelModeTestCase extends BazTestCase {
@Test
public void test() {
}
}
/**
* {@link DirtiesContext} is declared at the method level, without specifying
* the {@link DirtiesContext.HierarchyMode}.
* <p>After running this test class, the context cache should be <em>exhaustively</em>
* cleared beginning from the current context hierarchy, upwards to the highest
* parent context, and then back down through all subhierarchies of the parent
* context.
*/
public static class MethodLevelDirtiesContextWithExhaustiveModeTestCase extends BazTestCase {
@Test
@DirtiesContext
public void test() {
}
}
/**
* {@link DirtiesContext} is declared at the method level, specifying the
* {@link DirtiesContext.HierarchyMode#CURRENT_LEVEL CURRENT_LEVEL} hierarchy mode.
* <p>After running this test class, the context cache should be cleared
* beginning from the current context hierarchy and down through all subhierarchies.
*/
public static class MethodLevelDirtiesContextWithCurrentLevelModeTestCase extends BazTestCase {
@Test
@DirtiesContext(hierarchyMode = HierarchyMode.CURRENT_LEVEL)
public void test() {
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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,13 +16,16 @@
package org.springframework.test.context;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.springframework.test.context.ContextLoaderUtils.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Test;
@@ -105,6 +108,233 @@ public class ContextLoaderUtilsTests {
assertEquals(expectedInitializerClasses, mergedConfig.getContextInitializerClasses());
}
private void debugConfigAttributes(List<ContextConfigurationAttributes> configAttributesList) {
// for (ContextConfigurationAttributes configAttributes : configAttributesList) {
// System.err.println(configAttributes);
// }
}
@Test(expected = IllegalStateException.class)
public void resolveContextHierarchyAttributesForSingleTestClassWithContextConfigurationAndContextHierarchy() {
resolveContextHierarchyAttributes(SingleTestClassWithContextConfigurationAndContextHierarchy.class);
}
@Test
public void resolveContextHierarchyAttributesForSingleTestClassWithImplicitSingleLevelContextHierarchy() {
List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(BareAnnotations.class);
assertEquals(1, hierarchyAttributes.size());
List<ContextConfigurationAttributes> configAttributesList = hierarchyAttributes.get(0);
assertEquals(1, configAttributesList.size());
debugConfigAttributes(configAttributesList);
}
@Test
public void resolveContextHierarchyAttributesForSingleTestClassWithSingleLevelContextHierarchy() {
List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(SingleTestClassWithSingleLevelContextHierarchy.class);
assertEquals(1, hierarchyAttributes.size());
List<ContextConfigurationAttributes> configAttributesList = hierarchyAttributes.get(0);
assertEquals(1, configAttributesList.size());
debugConfigAttributes(configAttributesList);
}
@Test
public void resolveContextHierarchyAttributesForSingleTestClassWithTripleLevelContextHierarchy() {
List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(SingleTestClassWithTripleLevelContextHierarchy.class);
assertEquals(1, hierarchyAttributes.size());
List<ContextConfigurationAttributes> configAttributesList = hierarchyAttributes.get(0);
assertEquals(3, configAttributesList.size());
debugConfigAttributes(configAttributesList);
}
@Test
public void resolveContextHierarchyAttributesForTestClassHierarchyWithSingleLevelContextHierarchies() {
List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass3WithSingleLevelContextHierarchy.class);
assertEquals(3, hierarchyAttributes.size());
List<ContextConfigurationAttributes> configAttributesListClassLevel1 = hierarchyAttributes.get(0);
debugConfigAttributes(configAttributesListClassLevel1);
assertEquals(1, configAttributesListClassLevel1.size());
assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("one.xml"));
List<ContextConfigurationAttributes> configAttributesListClassLevel2 = hierarchyAttributes.get(1);
debugConfigAttributes(configAttributesListClassLevel2);
assertEquals(1, configAttributesListClassLevel2.size());
assertArrayEquals(new String[] { "two-A.xml", "two-B.xml" },
configAttributesListClassLevel2.get(0).getLocations());
List<ContextConfigurationAttributes> configAttributesListClassLevel3 = hierarchyAttributes.get(2);
debugConfigAttributes(configAttributesListClassLevel3);
assertEquals(1, configAttributesListClassLevel3.size());
assertThat(configAttributesListClassLevel3.get(0).getLocations()[0], equalTo("three.xml"));
}
@Test
public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareContextConfigurationInSubclass() {
List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSubclass.class);
assertEquals(2, hierarchyAttributes.size());
List<ContextConfigurationAttributes> configAttributesListClassLevel1 = hierarchyAttributes.get(0);
debugConfigAttributes(configAttributesListClassLevel1);
assertEquals(1, configAttributesListClassLevel1.size());
assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("one.xml"));
List<ContextConfigurationAttributes> configAttributesListClassLevel2 = hierarchyAttributes.get(1);
debugConfigAttributes(configAttributesListClassLevel2);
assertEquals(1, configAttributesListClassLevel2.size());
assertThat(configAttributesListClassLevel2.get(0).getLocations()[0], equalTo("two.xml"));
}
@Test
public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareContextConfigurationInSuperclass() {
List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSuperclass.class);
assertEquals(2, hierarchyAttributes.size());
List<ContextConfigurationAttributes> configAttributesListClassLevel1 = hierarchyAttributes.get(0);
debugConfigAttributes(configAttributesListClassLevel1);
assertEquals(1, configAttributesListClassLevel1.size());
assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("one.xml"));
List<ContextConfigurationAttributes> configAttributesListClassLevel2 = hierarchyAttributes.get(1);
debugConfigAttributes(configAttributesListClassLevel2);
assertEquals(1, configAttributesListClassLevel2.size());
assertThat(configAttributesListClassLevel2.get(0).getLocations()[0], equalTo("two.xml"));
}
@Test
public void resolveContextHierarchyAttributesForTestClassHierarchyWithMultiLevelContextHierarchies() {
List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass3WithMultiLevelContextHierarchy.class);
assertEquals(3, hierarchyAttributes.size());
List<ContextConfigurationAttributes> configAttributesListClassLevel1 = hierarchyAttributes.get(0);
debugConfigAttributes(configAttributesListClassLevel1);
assertEquals(2, configAttributesListClassLevel1.size());
assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("1-A.xml"));
assertThat(configAttributesListClassLevel1.get(1).getLocations()[0], equalTo("1-B.xml"));
List<ContextConfigurationAttributes> configAttributesListClassLevel2 = hierarchyAttributes.get(1);
debugConfigAttributes(configAttributesListClassLevel2);
assertEquals(2, configAttributesListClassLevel2.size());
assertThat(configAttributesListClassLevel2.get(0).getLocations()[0], equalTo("2-A.xml"));
assertThat(configAttributesListClassLevel2.get(1).getLocations()[0], equalTo("2-B.xml"));
List<ContextConfigurationAttributes> configAttributesListClassLevel3 = hierarchyAttributes.get(2);
debugConfigAttributes(configAttributesListClassLevel3);
assertEquals(3, configAttributesListClassLevel3.size());
assertThat(configAttributesListClassLevel3.get(0).getLocations()[0], equalTo("3-A.xml"));
assertThat(configAttributesListClassLevel3.get(1).getLocations()[0], equalTo("3-B.xml"));
assertThat(configAttributesListClassLevel3.get(2).getLocations()[0], equalTo("3-C.xml"));
}
private void assertContextConfigEntriesAreNotUnique(Class<?> testClass) {
try {
resolveContextHierarchyAttributes(testClass);
fail("Should throw an IllegalStateException");
}
catch (IllegalStateException e) {
String msg = String.format(
"The @ContextConfiguration elements configured via @ContextHierarchy in test class [%s] must define unique contexts to load.",
testClass.getName());
assertEquals(msg, e.getMessage());
}
}
@Test
public void resolveContextHierarchyAttributesForSingleTestClassWithMultiLevelContextHierarchyWithEmptyContextConfig() {
assertContextConfigEntriesAreNotUnique(SingleTestClassWithMultiLevelContextHierarchyWithEmptyContextConfig.class);
}
@Test
public void resolveContextHierarchyAttributesForSingleTestClassWithMultiLevelContextHierarchyWithDuplicatedContextConfig() {
assertContextConfigEntriesAreNotUnique(SingleTestClassWithMultiLevelContextHierarchyWithDuplicatedContextConfig.class);
}
@Test
public void buildContextHierarchyMapForTestClassHierarchyWithMultiLevelContextHierarchies() {
Map<String, List<ContextConfigurationAttributes>> map = buildContextHierarchyMap(TestClass3WithMultiLevelContextHierarchy.class);
assertThat(map.size(), is(3));
assertThat(map.keySet(), hasItems("alpha", "beta", "gamma"));
List<ContextConfigurationAttributes> alphaConfig = map.get("alpha");
assertThat(alphaConfig.size(), is(3));
assertThat(alphaConfig.get(0).getLocations()[0], is("1-A.xml"));
assertThat(alphaConfig.get(1).getLocations()[0], is("2-A.xml"));
assertThat(alphaConfig.get(2).getLocations()[0], is("3-A.xml"));
List<ContextConfigurationAttributes> betaConfig = map.get("beta");
assertThat(betaConfig.size(), is(3));
assertThat(betaConfig.get(0).getLocations()[0], is("1-B.xml"));
assertThat(betaConfig.get(1).getLocations()[0], is("2-B.xml"));
assertThat(betaConfig.get(2).getLocations()[0], is("3-B.xml"));
List<ContextConfigurationAttributes> gammaConfig = map.get("gamma");
assertThat(gammaConfig.size(), is(1));
assertThat(gammaConfig.get(0).getLocations()[0], is("3-C.xml"));
}
@Test
public void buildContextHierarchyMapForTestClassHierarchyWithMultiLevelContextHierarchiesAndUnnamedConfig() {
Map<String, List<ContextConfigurationAttributes>> map = buildContextHierarchyMap(TestClass3WithMultiLevelContextHierarchyAndUnnamedConfig.class);
String level1 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 1;
String level2 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 2;
String level3 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 3;
String level4 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 4;
String level5 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 5;
String level6 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 6;
String level7 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 7;
assertThat(map.size(), is(7));
assertThat(map.keySet(), hasItems(level1, level2, level3, level4, level5, level6, level7));
List<ContextConfigurationAttributes> level1Config = map.get(level1);
assertThat(level1Config.size(), is(1));
assertThat(level1Config.get(0).getLocations()[0], is("1-A.xml"));
List<ContextConfigurationAttributes> level2Config = map.get(level2);
assertThat(level2Config.size(), is(1));
assertThat(level2Config.get(0).getLocations()[0], is("1-B.xml"));
List<ContextConfigurationAttributes> level3Config = map.get(level3);
assertThat(level3Config.size(), is(1));
assertThat(level3Config.get(0).getLocations()[0], is("2-A.xml"));
// ...
List<ContextConfigurationAttributes> level7Config = map.get(level7);
assertThat(level7Config.size(), is(1));
assertThat(level7Config.get(0).getLocations()[0], is("3-C.xml"));
}
@Test
public void buildContextHierarchyMapForTestClassHierarchyWithMultiLevelContextHierarchiesAndPartiallyNamedConfig() {
Map<String, List<ContextConfigurationAttributes>> map = buildContextHierarchyMap(TestClass2WithMultiLevelContextHierarchyAndPartiallyNamedConfig.class);
String level1 = "parent";
String level2 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 2;
String level3 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 3;
assertThat(map.size(), is(3));
assertThat(map.keySet(), hasItems(level1, level2, level3));
Iterator<String> levels = map.keySet().iterator();
assertThat(levels.next(), is(level1));
assertThat(levels.next(), is(level2));
assertThat(levels.next(), is(level3));
List<ContextConfigurationAttributes> level1Config = map.get(level1);
assertThat(level1Config.size(), is(2));
assertThat(level1Config.get(0).getLocations()[0], is("1-A.xml"));
assertThat(level1Config.get(1).getLocations()[0], is("2-A.xml"));
List<ContextConfigurationAttributes> level2Config = map.get(level2);
assertThat(level2Config.size(), is(1));
assertThat(level2Config.get(0).getLocations()[0], is("1-B.xml"));
List<ContextConfigurationAttributes> level3Config = map.get(level3);
assertThat(level3Config.size(), is(1));
assertThat(level3Config.get(0).getLocations()[0], is("2-C.xml"));
}
@Test(expected = IllegalStateException.class)
public void resolveConfigAttributesWithConflictingLocations() {
resolveContextConfigurationAttributes(ConflictingLocations.class);
@@ -155,13 +385,13 @@ public class ContextLoaderUtilsTests {
@Test(expected = IllegalArgumentException.class)
public void buildMergedConfigWithoutAnnotation() {
buildMergedContextConfiguration(Enigma.class, null);
buildMergedContextConfiguration(Enigma.class, null, null);
}
@Test
public void buildMergedConfigWithBareAnnotations() {
Class<BareAnnotations> testClass = BareAnnotations.class;
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
assertMergedConfig(
mergedConfig,
@@ -173,7 +403,7 @@ public class ContextLoaderUtilsTests {
@Test
public void buildMergedConfigWithLocalAnnotationAndLocations() {
Class<?> testClass = LocationsFoo.class;
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
assertMergedConfig(mergedConfig, testClass, new String[] { "classpath:/foo.xml" }, EMPTY_CLASS_ARRAY,
DelegatingSmartContextLoader.class);
@@ -182,7 +412,7 @@ public class ContextLoaderUtilsTests {
@Test
public void buildMergedConfigWithLocalAnnotationAndClasses() {
Class<?> testClass = ClassesFoo.class;
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, new Class<?>[] { FooConfig.class },
DelegatingSmartContextLoader.class);
@@ -193,7 +423,7 @@ public class ContextLoaderUtilsTests {
Class<?> testClass = LocationsFoo.class;
Class<? extends ContextLoader> expectedContextLoaderClass = GenericPropertiesContextLoader.class;
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass,
expectedContextLoaderClass.getName());
expectedContextLoaderClass.getName(), null);
assertMergedConfig(mergedConfig, testClass, new String[] { "classpath:/foo.xml" }, EMPTY_CLASS_ARRAY,
expectedContextLoaderClass);
@@ -204,7 +434,7 @@ public class ContextLoaderUtilsTests {
Class<?> testClass = ClassesFoo.class;
Class<? extends ContextLoader> expectedContextLoaderClass = GenericPropertiesContextLoader.class;
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass,
expectedContextLoaderClass.getName());
expectedContextLoaderClass.getName(), null);
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, new Class<?>[] { FooConfig.class },
expectedContextLoaderClass);
@@ -215,7 +445,7 @@ public class ContextLoaderUtilsTests {
Class<?> testClass = LocationsBar.class;
String[] expectedLocations = new String[] { "/foo.xml", "/bar.xml" };
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
assertMergedConfig(mergedConfig, testClass, expectedLocations, EMPTY_CLASS_ARRAY,
AnnotationConfigContextLoader.class);
}
@@ -225,7 +455,7 @@ public class ContextLoaderUtilsTests {
Class<?> testClass = ClassesBar.class;
Class<?>[] expectedClasses = new Class<?>[] { FooConfig.class, BarConfig.class };
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses,
AnnotationConfigContextLoader.class);
}
@@ -235,7 +465,7 @@ public class ContextLoaderUtilsTests {
Class<?> testClass = OverriddenLocationsBar.class;
String[] expectedLocations = new String[] { "/bar.xml" };
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
assertMergedConfig(mergedConfig, testClass, expectedLocations, EMPTY_CLASS_ARRAY,
AnnotationConfigContextLoader.class);
}
@@ -245,7 +475,7 @@ public class ContextLoaderUtilsTests {
Class<?> testClass = OverriddenClassesBar.class;
Class<?>[] expectedClasses = new Class<?>[] { BarConfig.class };
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses,
AnnotationConfigContextLoader.class);
}
@@ -258,7 +488,7 @@ public class ContextLoaderUtilsTests {
= new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
expectedInitializerClasses.add(FooInitializer.class);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
DelegatingSmartContextLoader.class);
}
@@ -272,7 +502,7 @@ public class ContextLoaderUtilsTests {
expectedInitializerClasses.add(FooInitializer.class);
expectedInitializerClasses.add(BarInitializer.class);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
DelegatingSmartContextLoader.class);
}
@@ -285,7 +515,7 @@ public class ContextLoaderUtilsTests {
= new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
expectedInitializerClasses.add(BarInitializer.class);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
DelegatingSmartContextLoader.class);
}
@@ -298,7 +528,7 @@ public class ContextLoaderUtilsTests {
= new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
expectedInitializerClasses.add(BarInitializer.class);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
DelegatingSmartContextLoader.class);
}
@@ -377,6 +607,8 @@ public class ContextLoaderUtilsTests {
}
// -------------------------------------------------------------------------
private static class Enigma {
}
@@ -475,4 +707,140 @@ public class ContextLoaderUtilsTests {
private static class OverriddenInitializersAndClassesBar extends InitializersFoo {
}
@ContextConfiguration("foo.xml")
@ContextHierarchy(@ContextConfiguration("bar.xml"))
private static class SingleTestClassWithContextConfigurationAndContextHierarchy {
}
@ContextHierarchy(@ContextConfiguration("A.xml"))
private static class SingleTestClassWithSingleLevelContextHierarchy {
}
@ContextHierarchy({//
//
@ContextConfiguration("A.xml"),//
@ContextConfiguration("B.xml"),//
@ContextConfiguration("C.xml") //
})
private static class SingleTestClassWithTripleLevelContextHierarchy {
}
@ContextHierarchy(@ContextConfiguration("one.xml"))
private static class TestClass1WithSingleLevelContextHierarchy {
}
@ContextHierarchy(@ContextConfiguration({ "two-A.xml", "two-B.xml" }))
private static class TestClass2WithSingleLevelContextHierarchy extends TestClass1WithSingleLevelContextHierarchy {
}
@ContextHierarchy(@ContextConfiguration("three.xml"))
private static class TestClass3WithSingleLevelContextHierarchy extends TestClass2WithSingleLevelContextHierarchy {
}
@ContextConfiguration("one.xml")
private static class TestClass1WithBareContextConfigurationInSuperclass {
}
@ContextHierarchy(@ContextConfiguration("two.xml"))
private static class TestClass2WithBareContextConfigurationInSuperclass extends
TestClass1WithBareContextConfigurationInSuperclass {
}
@ContextHierarchy(@ContextConfiguration("one.xml"))
private static class TestClass1WithBareContextConfigurationInSubclass {
}
@ContextConfiguration("two.xml")
private static class TestClass2WithBareContextConfigurationInSubclass extends
TestClass1WithBareContextConfigurationInSuperclass {
}
@ContextHierarchy({//
//
@ContextConfiguration(locations = "1-A.xml", name = "alpha"),//
@ContextConfiguration(locations = "1-B.xml", name = "beta") //
})
private static class TestClass1WithMultiLevelContextHierarchy {
}
@ContextHierarchy({//
//
@ContextConfiguration(locations = "2-A.xml", name = "alpha"),//
@ContextConfiguration(locations = "2-B.xml", name = "beta") //
})
private static class TestClass2WithMultiLevelContextHierarchy extends TestClass1WithMultiLevelContextHierarchy {
}
@ContextHierarchy({//
//
@ContextConfiguration(locations = "3-A.xml", name = "alpha"),//
@ContextConfiguration(locations = "3-B.xml", name = "beta"),//
@ContextConfiguration(locations = "3-C.xml", name = "gamma") //
})
private static class TestClass3WithMultiLevelContextHierarchy extends TestClass2WithMultiLevelContextHierarchy {
}
@ContextHierarchy({//
//
@ContextConfiguration(locations = "1-A.xml"),//
@ContextConfiguration(locations = "1-B.xml") //
})
private static class TestClass1WithMultiLevelContextHierarchyAndUnnamedConfig {
}
@ContextHierarchy({//
//
@ContextConfiguration(locations = "2-A.xml"),//
@ContextConfiguration(locations = "2-B.xml") //
})
private static class TestClass2WithMultiLevelContextHierarchyAndUnnamedConfig extends
TestClass1WithMultiLevelContextHierarchyAndUnnamedConfig {
}
@ContextHierarchy({//
//
@ContextConfiguration(locations = "3-A.xml"),//
@ContextConfiguration(locations = "3-B.xml"),//
@ContextConfiguration(locations = "3-C.xml") //
})
private static class TestClass3WithMultiLevelContextHierarchyAndUnnamedConfig extends
TestClass2WithMultiLevelContextHierarchyAndUnnamedConfig {
}
@ContextHierarchy({//
//
@ContextConfiguration(locations = "1-A.xml", name = "parent"),//
@ContextConfiguration(locations = "1-B.xml") //
})
private static class TestClass1WithMultiLevelContextHierarchyAndPartiallyNamedConfig {
}
@ContextHierarchy({//
//
@ContextConfiguration(locations = "2-A.xml", name = "parent"),//
@ContextConfiguration(locations = "2-C.xml") //
})
private static class TestClass2WithMultiLevelContextHierarchyAndPartiallyNamedConfig extends
TestClass1WithMultiLevelContextHierarchyAndPartiallyNamedConfig {
}
@ContextHierarchy({
//
@ContextConfiguration,//
@ContextConfiguration //
})
private static class SingleTestClassWithMultiLevelContextHierarchyWithEmptyContextConfig {
}
@ContextHierarchy({
//
@ContextConfiguration("foo.xml"),//
@ContextConfiguration(classes = BarConfig.class),// duplicate!
@ContextConfiguration("baz.xml"),//
@ContextConfiguration(classes = BarConfig.class),// duplicate!
@ContextConfiguration(loader = AnnotationConfigContextLoader.class) //
})
private static class SingleTestClassWithMultiLevelContextHierarchyWithDuplicatedContextConfig {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -76,7 +76,7 @@ public class MergedContextConfigurationTests {
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, new AnnotationConfigContextLoader());
assertFalse(mergedConfig1.hashCode() == mergedConfig2.hashCode());
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
}
@Test
@@ -97,7 +97,7 @@ public class MergedContextConfigurationTests {
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), locations2,
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
assertFalse(mergedConfig1.hashCode() == mergedConfig2.hashCode());
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
}
@Test
@@ -118,7 +118,7 @@ public class MergedContextConfigurationTests {
classes1, EMPTY_STRING_ARRAY, loader);
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
classes2, EMPTY_STRING_ARRAY, loader);
assertFalse(mergedConfig1.hashCode() == mergedConfig2.hashCode());
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
}
@Test
@@ -161,7 +161,7 @@ public class MergedContextConfigurationTests {
EMPTY_CLASS_ARRAY, activeProfiles1, loader);
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, activeProfiles2, loader);
assertFalse(mergedConfig1.hashCode() == mergedConfig2.hashCode());
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
}
@Test
@@ -197,15 +197,47 @@ public class MergedContextConfigurationTests {
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());
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
}
/**
* @since 3.2.2
*/
@Test
public void hashCodeWithSameParent() {
MergedContextConfiguration parent = new MergedContextConfiguration(getClass(), new String[] { "foo", "bar}" },
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent);
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent);
assertEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
}
/**
* @since 3.2.2
*/
@Test
public void hashCodeWithDifferentParents() {
MergedContextConfiguration parent1 = new MergedContextConfiguration(getClass(), new String[] { "foo", "bar}" },
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
MergedContextConfiguration parent2 = new MergedContextConfiguration(getClass(), new String[] { "baz", "quux" },
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent1);
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent2);
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
}
@Test
public void equalsBasics() {
MergedContextConfiguration mergedConfig = new MergedContextConfiguration(null, null, null, null, null);
assertTrue(mergedConfig.equals(mergedConfig));
assertFalse(mergedConfig.equals(null));
assertFalse(mergedConfig.equals(new Integer(1)));
assertEquals(mergedConfig, mergedConfig);
assertNotEquals(mergedConfig, null);
assertNotEquals(mergedConfig, new Integer(1));
}
@Test
@@ -237,8 +269,8 @@ public class MergedContextConfigurationTests {
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, new AnnotationConfigContextLoader());
assertFalse(mergedConfig1.equals(mergedConfig2));
assertFalse(mergedConfig2.equals(mergedConfig1));
assertNotEquals(mergedConfig1, mergedConfig2);
assertNotEquals(mergedConfig2, mergedConfig1);
}
@Test
@@ -259,8 +291,8 @@ public class MergedContextConfigurationTests {
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), locations2,
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
assertFalse(mergedConfig1.equals(mergedConfig2));
assertFalse(mergedConfig2.equals(mergedConfig1));
assertNotEquals(mergedConfig1, mergedConfig2);
assertNotEquals(mergedConfig2, mergedConfig1);
}
@Test
@@ -281,8 +313,8 @@ public class MergedContextConfigurationTests {
classes1, EMPTY_STRING_ARRAY, loader);
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
classes2, EMPTY_STRING_ARRAY, loader);
assertFalse(mergedConfig1.equals(mergedConfig2));
assertFalse(mergedConfig2.equals(mergedConfig1));
assertNotEquals(mergedConfig1, mergedConfig2);
assertNotEquals(mergedConfig2, mergedConfig1);
}
@Test
@@ -325,8 +357,8 @@ public class MergedContextConfigurationTests {
EMPTY_CLASS_ARRAY, activeProfiles1, loader);
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, activeProfiles2, loader);
assertFalse(mergedConfig1.equals(mergedConfig2));
assertFalse(mergedConfig2.equals(mergedConfig1));
assertNotEquals(mergedConfig1, mergedConfig2);
assertNotEquals(mergedConfig2, mergedConfig1);
}
@Test
@@ -362,8 +394,42 @@ public class MergedContextConfigurationTests {
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));
assertNotEquals(mergedConfig1, mergedConfig2);
assertNotEquals(mergedConfig2, mergedConfig1);
}
/**
* @since 3.2.2
*/
@Test
public void equalsWithSameParent() {
MergedContextConfiguration parent = new MergedContextConfiguration(getClass(), new String[] { "foo", "bar}" },
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent);
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent);
assertEquals(mergedConfig1, mergedConfig2);
assertEquals(mergedConfig2, mergedConfig1);
}
/**
* @since 3.2.2
*/
@Test
public void equalsWithDifferentParents() {
MergedContextConfiguration parent1 = new MergedContextConfiguration(getClass(), new String[] { "foo", "bar}" },
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
MergedContextConfiguration parent2 = new MergedContextConfiguration(getClass(), new String[] { "baz", "quux" },
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent1);
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent2);
assertNotEquals(mergedConfig1, mergedConfig2);
assertNotEquals(mergedConfig2, mergedConfig1);
}

View File

@@ -44,7 +44,7 @@ import org.springframework.test.context.support.DirtiesContextTestExecutionListe
* @author Sam Brannen
* @author Juergen Hoeller
* @since 2.5
* @see TestContextCacheKeyTests
* @see ContextCacheTests
*/
@RunWith(OrderedMethodsSpringJUnit4ClassRunner.class)
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class })

View File

@@ -1,102 +0,0 @@
/*
* 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;
import static org.junit.Assert.assertNotNull;
import static org.springframework.test.context.SpringRunnerContextCacheTests.assertContextCacheStatistics;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
/**
* Unit tests for verifying proper behavior of the {@link ContextCache} in
* conjunction with cache keys used in {@link TestContext}.
*
* @author Sam Brannen
* @since 3.1
* @see SpringRunnerContextCacheTests
*/
public class TestContextCacheKeyTests {
private ContextCache contextCache = new ContextCache();
@Before
public void initialCacheState() {
assertContextCacheStatistics(contextCache, "initial state", 0, 0, 0);
}
private void loadAppCtxAndAssertCacheStats(Class<?> testClass, int expectedSize, int expectedHitCount,
int expectedMissCount) {
TestContext testContext = new TestContext(testClass, contextCache);
ApplicationContext context = testContext.getApplicationContext();
assertNotNull(context);
assertContextCacheStatistics(contextCache, testClass.getName(), expectedSize, expectedHitCount,
expectedMissCount);
}
@Test
public void verifyCacheKeyIsBasedOnContextLoader() {
loadAppCtxAndAssertCacheStats(AnnotationConfigContextLoaderTestCase.class, 1, 0, 1);
loadAppCtxAndAssertCacheStats(AnnotationConfigContextLoaderTestCase.class, 1, 1, 1);
loadAppCtxAndAssertCacheStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 1, 2);
loadAppCtxAndAssertCacheStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 2, 2);
loadAppCtxAndAssertCacheStats(AnnotationConfigContextLoaderTestCase.class, 2, 3, 2);
loadAppCtxAndAssertCacheStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 4, 2);
}
@Test
public void verifyCacheKeyIsBasedOnActiveProfiles() {
loadAppCtxAndAssertCacheStats(FooBarProfilesTestCase.class, 1, 0, 1);
loadAppCtxAndAssertCacheStats(FooBarProfilesTestCase.class, 1, 1, 1);
// Profiles {foo, bar} should hash to the same as {bar,foo}
loadAppCtxAndAssertCacheStats(BarFooProfilesTestCase.class, 1, 2, 1);
loadAppCtxAndAssertCacheStats(FooBarProfilesTestCase.class, 1, 3, 1);
loadAppCtxAndAssertCacheStats(FooBarProfilesTestCase.class, 1, 4, 1);
loadAppCtxAndAssertCacheStats(BarFooProfilesTestCase.class, 1, 5, 1);
}
@Configuration
static class Config {
}
@ContextConfiguration(classes = Config.class, loader = AnnotationConfigContextLoader.class)
private static class AnnotationConfigContextLoaderTestCase {
}
@ContextConfiguration(classes = Config.class, loader = CustomAnnotationConfigContextLoader.class)
private static class CustomAnnotationConfigContextLoaderTestCase {
}
private static class CustomAnnotationConfigContextLoader extends AnnotationConfigContextLoader {
}
@ActiveProfiles({ "foo", "bar" })
@ContextConfiguration(classes = Config.class, loader = AnnotationConfigContextLoader.class)
private static class FooBarProfilesTestCase {
}
@ActiveProfiles({ "bar", "foo" })
@ContextConfiguration(classes = Config.class, loader = AnnotationConfigContextLoader.class)
private static class BarFooProfilesTestCase {
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2002-2013 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.hierarchies.standard;
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.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Sam Brannen
* @since 3.2.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy({
//
@ContextConfiguration(name = "parent", classes = ClassHierarchyWithMergedConfigLevelOneTests.AppConfig.class),//
@ContextConfiguration(name = "child", classes = ClassHierarchyWithMergedConfigLevelOneTests.UserConfig.class) //
})
public class ClassHierarchyWithMergedConfigLevelOneTests {
@Configuration
static class AppConfig {
@Bean
public String parent() {
return "parent";
}
}
@Configuration
static class UserConfig {
@Autowired
private AppConfig appConfig;
@Bean
public String user() {
return appConfig.parent() + " + user";
}
@Bean
public String beanFromUserConfig() {
return "from UserConfig";
}
}
@Autowired
protected String parent;
@Autowired
protected String user;
@Autowired(required = false)
@Qualifier("beanFromUserConfig")
protected String beanFromUserConfig;
@Autowired
protected ApplicationContext context;
@Test
public void loadContextHierarchy() {
assertNotNull("child ApplicationContext", context);
assertNotNull("parent ApplicationContext", context.getParent());
assertNull("grandparent ApplicationContext", context.getParent().getParent());
assertEquals("parent", parent);
assertEquals("parent + user", user);
assertEquals("from UserConfig", beanFromUserConfig);
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2002-2013 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.hierarchies.standard;
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.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Sam Brannen
* @since 3.2.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy(@ContextConfiguration(name = "child", classes = ClassHierarchyWithMergedConfigLevelTwoTests.OrderConfig.class))
public class ClassHierarchyWithMergedConfigLevelTwoTests extends ClassHierarchyWithMergedConfigLevelOneTests {
@Configuration
static class OrderConfig {
@Autowired
private ClassHierarchyWithMergedConfigLevelOneTests.UserConfig userConfig;
@Bean
public String order() {
return userConfig.user() + " + order";
}
}
@Autowired
private String order;
@Test
@Override
public void loadContextHierarchy() {
super.loadContextHierarchy();
assertEquals("parent + user + order", order);
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2002-2013 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.hierarchies.standard;
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.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Sam Brannen
* @since 3.2.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy(@ContextConfiguration(name = "child", classes = ClassHierarchyWithOverriddenConfigLevelTwoTests.TestUserConfig.class, inheritLocations = false))
public class ClassHierarchyWithOverriddenConfigLevelTwoTests extends ClassHierarchyWithMergedConfigLevelOneTests {
@Configuration
static class TestUserConfig {
@Autowired
private ClassHierarchyWithMergedConfigLevelOneTests.AppConfig appConfig;
@Bean
public String user() {
return appConfig.parent() + " + test user";
}
@Bean
public String beanFromTestUserConfig() {
return "from TestUserConfig";
}
}
@Autowired
private String beanFromTestUserConfig;
@Test
@Override
public void loadContextHierarchy() {
assertNotNull("child ApplicationContext", context);
assertNotNull("parent ApplicationContext", context.getParent());
assertNull("grandparent ApplicationContext", context.getParent().getParent());
assertEquals("parent", parent);
assertEquals("parent + test user", user);
assertEquals("from TestUserConfig", beanFromTestUserConfig);
assertNull("Bean from UserConfig should not be present.", beanFromUserConfig);
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright 2002-2013 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.hierarchies.standard;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
/**
* Integration tests that verify support for {@link DirtiesContext.HierarchyMode}
* in conjunction with context hierarchies configured via {@link ContextHierarchy}.
*
* <p>Note that correct method execution order is essential, thus the use of
* {@link FixMethodOrder}.
*
* @author Sam Brannen
* @since 3.2.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy({ @ContextConfiguration(classes = DirtiesContextWithContextHierarchyTests.ParentConfig.class),
@ContextConfiguration(classes = DirtiesContextWithContextHierarchyTests.ChildConfig.class) })
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class DirtiesContextWithContextHierarchyTests {
@Configuration
static class ParentConfig {
@Bean
public StringBuffer foo() {
return new StringBuffer("foo");
}
@Bean
public StringBuffer baz() {
return new StringBuffer("baz-parent");
}
}
@Configuration
static class ChildConfig {
@Bean
public StringBuffer baz() {
return new StringBuffer("baz-child");
}
}
@Autowired
private StringBuffer foo;
@Autowired
private StringBuffer baz;
@Autowired
private ApplicationContext context;
// -------------------------------------------------------------------------
private void reverseStringBuffers() {
foo.reverse();
baz.reverse();
}
private void assertOriginalState() {
assertCleanParentContext();
assertCleanChildContext();
}
private void assertCleanParentContext() {
assertEquals("foo", foo.toString());
}
private void assertCleanChildContext() {
assertEquals("baz-child", baz.toString());
}
private void assertDirtyParentContext() {
assertEquals("oof", foo.toString());
}
private void assertDirtyChildContext() {
assertEquals("dlihc-zab", baz.toString());
}
// -------------------------------------------------------------------------
@Before
public void verifyContextHierarchy() {
assertNotNull("child ApplicationContext", context);
assertNotNull("parent ApplicationContext", context.getParent());
assertNull("grandparent ApplicationContext", context.getParent().getParent());
}
@Test
public void test1_verifyOriginalStateAndDirtyContexts() {
assertOriginalState();
reverseStringBuffers();
}
@Test
@DirtiesContext
public void test2_verifyContextsWereDirtiedAndTriggerExhaustiveCacheClearing() {
assertDirtyParentContext();
assertDirtyChildContext();
}
@Test
@DirtiesContext(hierarchyMode = HierarchyMode.CURRENT_LEVEL)
public void test3_verifyOriginalStateWasReinstatedAndDirtyContextsAndTriggerCurrentLevelCacheClearing() {
assertOriginalState();
reverseStringBuffers();
}
@Test
public void test4_verifyParentContextIsStillDirtyButChildContextHasBeenReinstated() {
assertDirtyParentContext();
assertCleanChildContext();
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2002-2013 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.hierarchies.standard;
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.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Sam Brannen
* @since 3.2.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy(@ContextConfiguration)
public class SingleTestClassWithSingleLevelContextHierarchyTests {
@Configuration
static class Config {
@Bean
public String foo() {
return "foo";
}
}
@Autowired
private String foo;
@Autowired
private ApplicationContext context;
@Test
public void loadContextHierarchy() {
assertNotNull("child ApplicationContext", context);
assertNull("parent ApplicationContext", context.getParent());
assertEquals("foo", foo);
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2002-2013 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.hierarchies.standard;
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.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Sam Brannen
* @since 3.2.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy({
@ContextConfiguration(classes = SingleTestClassWithTwoLevelContextHierarchyAndMixedConfigTypesTests.ParentConfig.class),
@ContextConfiguration("SingleTestClassWithTwoLevelContextHierarchyAndMixedConfigTypesTests-ChildConfig.xml") })
public class SingleTestClassWithTwoLevelContextHierarchyAndMixedConfigTypesTests {
@Configuration
static class ParentConfig {
@Bean
public String foo() {
return "foo";
}
@Bean
public String baz() {
return "baz-parent";
}
}
@Autowired
private String foo;
@Autowired
private String bar;
@Autowired
private String baz;
@Autowired
private ApplicationContext context;
@Test
public void loadContextHierarchy() {
assertNotNull("child ApplicationContext", context);
assertNotNull("parent ApplicationContext", context.getParent());
assertNull("grandparent ApplicationContext", context.getParent().getParent());
assertEquals("foo", foo);
assertEquals("bar", bar);
assertEquals("baz-child", baz);
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2002-2013 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.hierarchies.standard;
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.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Sam Brannen
* @since 3.2.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy({
@ContextConfiguration(classes = SingleTestClassWithTwoLevelContextHierarchyTests.ParentConfig.class),
@ContextConfiguration(classes = SingleTestClassWithTwoLevelContextHierarchyTests.ChildConfig.class) })
public class SingleTestClassWithTwoLevelContextHierarchyTests {
@Configuration
static class ParentConfig {
@Bean
public String foo() {
return "foo";
}
@Bean
public String baz() {
return "baz-parent";
}
}
@Configuration
static class ChildConfig {
@Bean
public String bar() {
return "bar";
}
@Bean
public String baz() {
return "baz-child";
}
}
@Autowired
private String foo;
@Autowired
private String bar;
@Autowired
private String baz;
@Autowired
private ApplicationContext context;
@Test
public void loadContextHierarchy() {
assertNotNull("child ApplicationContext", context);
assertNotNull("parent ApplicationContext", context.getParent());
assertNull("grandparent ApplicationContext", context.getParent().getParent());
assertEquals("foo", foo);
assertEquals("bar", bar);
assertEquals("baz-child", baz);
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2002-2013 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.hierarchies.standard;
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.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Sam Brannen
* @since 3.2.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy(@ContextConfiguration)
public class TestHierarchyLevelOneWithBareContextConfigurationInSubclassTests {
@Configuration
static class Config {
@Bean
public String foo() {
return "foo-level-1";
}
@Bean
public String bar() {
return "bar";
}
}
@Autowired
private String foo;
@Autowired
private String bar;
@Autowired
private ApplicationContext context;
@Test
public void loadContextHierarchy() {
assertNotNull("child ApplicationContext", context);
assertNull("parent ApplicationContext", context.getParent());
assertEquals("foo-level-1", foo);
assertEquals("bar", bar);
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2002-2013 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.hierarchies.standard;
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.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Sam Brannen
* @since 3.2.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TestHierarchyLevelOneWithBareContextConfigurationInSuperclassTests {
@Configuration
static class Config {
@Bean
public String foo() {
return "foo-level-1";
}
@Bean
public String bar() {
return "bar";
}
}
@Autowired
private String foo;
@Autowired
private String bar;
@Autowired
private ApplicationContext context;
@Test
public void loadContextHierarchy() {
assertNotNull("child ApplicationContext", context);
assertNull("parent ApplicationContext", context.getParent());
assertEquals("foo-level-1", foo);
assertEquals("bar", bar);
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2002-2013 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.hierarchies.standard;
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.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Sam Brannen
* @since 3.2.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy(@ContextConfiguration)
public class TestHierarchyLevelOneWithSingleLevelContextHierarchyTests {
@Configuration
static class Config {
@Bean
public String foo() {
return "foo-level-1";
}
@Bean
public String bar() {
return "bar";
}
}
@Autowired
private String foo;
@Autowired
private String bar;
@Autowired
private ApplicationContext context;
@Test
public void loadContextHierarchy() {
assertNotNull("child ApplicationContext", context);
assertNull("parent ApplicationContext", context.getParent());
assertEquals("foo-level-1", foo);
assertEquals("bar", bar);
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2002-2013 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.hierarchies.standard;
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.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Sam Brannen
* @since 3.2.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TestHierarchyLevelTwoWithBareContextConfigurationInSubclassTests extends
TestHierarchyLevelOneWithBareContextConfigurationInSubclassTests {
@Configuration
static class Config {
@Bean
public String foo() {
return "foo-level-2";
}
@Bean
public String baz() {
return "baz";
}
}
@Autowired
private String foo;
@Autowired
private String bar;
@Autowired
private String baz;
@Autowired
private ApplicationContext context;
@Test
@Override
public void loadContextHierarchy() {
assertNotNull("child ApplicationContext", context);
assertNotNull("parent ApplicationContext", context.getParent());
assertNull("grandparent ApplicationContext", context.getParent().getParent());
assertEquals("foo-level-2", foo);
assertEquals("bar", bar);
assertEquals("baz", baz);
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2002-2013 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.hierarchies.standard;
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.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Sam Brannen
* @since 3.2.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy(@ContextConfiguration)
public class TestHierarchyLevelTwoWithBareContextConfigurationInSuperclassTests extends
TestHierarchyLevelOneWithBareContextConfigurationInSuperclassTests {
@Configuration
static class Config {
@Bean
public String foo() {
return "foo-level-2";
}
@Bean
public String baz() {
return "baz";
}
}
@Autowired
private String foo;
@Autowired
private String bar;
@Autowired
private String baz;
@Autowired
private ApplicationContext context;
@Test
@Override
public void loadContextHierarchy() {
assertNotNull("child ApplicationContext", context);
assertNotNull("parent ApplicationContext", context.getParent());
assertNull("grandparent ApplicationContext", context.getParent().getParent());
assertEquals("foo-level-2", foo);
assertEquals("bar", bar);
assertEquals("baz", baz);
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2002-2013 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.hierarchies.standard;
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.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Sam Brannen
* @since 3.2.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy(@ContextConfiguration)
public class TestHierarchyLevelTwoWithSingleLevelContextHierarchyAndMixedConfigTypesTests extends
TestHierarchyLevelOneWithSingleLevelContextHierarchyTests {
@Autowired
private String foo;
@Autowired
private String bar;
@Autowired
private String baz;
@Autowired
private ApplicationContext context;
@Test
@Override
public void loadContextHierarchy() {
assertNotNull("child ApplicationContext", context);
assertNotNull("parent ApplicationContext", context.getParent());
assertNull("grandparent ApplicationContext", context.getParent().getParent());
assertEquals("foo-level-2", foo);
assertEquals("bar", bar);
assertEquals("baz", baz);
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2002-2013 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.hierarchies.standard;
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.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Sam Brannen
* @since 3.2.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy(@ContextConfiguration)
public class TestHierarchyLevelTwoWithSingleLevelContextHierarchyTests extends
TestHierarchyLevelOneWithSingleLevelContextHierarchyTests {
@Configuration
static class Config {
@Bean
public String foo() {
return "foo-level-2";
}
@Bean
public String baz() {
return "baz";
}
}
@Autowired
private String foo;
@Autowired
private String bar;
@Autowired
private String baz;
@Autowired
private ApplicationContext context;
@Test
@Override
public void loadContextHierarchy() {
assertNotNull("child ApplicationContext", context);
assertNotNull("parent ApplicationContext", context.getParent());
assertEquals("foo-level-2", foo);
assertEquals("bar", bar);
assertEquals("baz", baz);
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2002-2013 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.hierarchies.web;
import javax.servlet.ServletContext;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.hierarchies.web.ControllerIntegrationTests.AppConfig;
import org.springframework.test.context.hierarchies.web.ControllerIntegrationTests.WebConfig;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.context.WebApplicationContext;
import static org.junit.Assert.*;
/**
* @author Sam Brannen
* @since 3.2.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextHierarchy({
//
@ContextConfiguration(name = "root", classes = AppConfig.class),
@ContextConfiguration(name = "dispatcher", classes = WebConfig.class) //
})
public class ControllerIntegrationTests {
@Configuration
static class AppConfig {
@Bean
public String foo() {
return "foo";
}
}
@Configuration
static class WebConfig {
@Bean
public String bar() {
return "bar";
}
}
// -------------------------------------------------------------------------
@Autowired
private WebApplicationContext wac;
@Autowired
private String foo;
@Autowired
private String bar;
@Test
public void verifyRootWacSupport() {
assertEquals("foo", foo);
assertEquals("bar", bar);
ApplicationContext parent = wac.getParent();
assertNotNull(parent);
assertTrue(parent instanceof WebApplicationContext);
WebApplicationContext root = (WebApplicationContext) parent;
assertFalse(root.getBeansOfType(String.class).containsKey("bar"));
ServletContext childServletContext = wac.getServletContext();
assertNotNull(childServletContext);
ServletContext rootServletContext = root.getServletContext();
assertNotNull(rootServletContext);
assertSame(childServletContext, rootServletContext);
assertSame(root, rootServletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));
assertSame(root, childServletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2002-2013 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.hierarchies.web;
import javax.servlet.ServletContext;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.web.context.WebApplicationContext;
import static org.junit.Assert.*;
/**
* @author Sam Brannen
* @since 3.2.2
*/
@ContextHierarchy(@ContextConfiguration)
public class DispatcherWacRootWacEarTests extends RootWacEarTests {
@Autowired
private WebApplicationContext wac;
@Autowired
private String ear;
@Autowired
private String root;
@Autowired
private String dispatcher;
@Test
@Override
public void verifyEarConfig() {
/* no-op */
}
@Test
@Override
public void verifyRootWacConfig() {
/* no-op */
}
@Test
public void verifyDispatcherWacConfig() {
ApplicationContext parent = wac.getParent();
assertNotNull(parent);
assertTrue(parent instanceof WebApplicationContext);
ApplicationContext grandParent = parent.getParent();
assertNotNull(grandParent);
assertFalse(grandParent instanceof WebApplicationContext);
ServletContext dispatcherServletContext = wac.getServletContext();
assertNotNull(dispatcherServletContext);
ServletContext rootServletContext = ((WebApplicationContext) parent).getServletContext();
assertNotNull(rootServletContext);
assertSame(dispatcherServletContext, rootServletContext);
assertSame(parent,
rootServletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));
assertSame(parent,
dispatcherServletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));
assertEquals("ear", ear);
assertEquals("root", root);
assertEquals("dispatcher", dispatcher);
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2002-2013 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.hierarchies.web;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.context.WebApplicationContext;
import static org.junit.Assert.*;
/**
* @author Sam Brannen
* @since 3.2.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class EarTests {
@Configuration
static class EarConfig {
@Bean
public String ear() {
return "ear";
}
}
// -------------------------------------------------------------------------
@Autowired
private ApplicationContext context;
@Autowired
private String ear;
@Test
public void verifyEarConfig() {
assertFalse(context instanceof WebApplicationContext);
assertNull(context.getParent());
assertEquals("ear", ear);
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2002-2013 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.hierarchies.web;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.context.WebApplicationContext;
import static org.junit.Assert.*;
/**
* @author Sam Brannen
* @since 3.2.2
*/
@WebAppConfiguration
@ContextHierarchy(@ContextConfiguration)
public class RootWacEarTests extends EarTests {
@Configuration
static class RootWacConfig {
@Bean
public String root() {
return "root";
}
}
// -------------------------------------------------------------------------
@Autowired
private WebApplicationContext wac;
@Autowired
private String ear;
@Autowired
private String root;
@Test
@Override
public void verifyEarConfig() {
/* no-op */
}
@Test
public void verifyRootWacConfig() {
ApplicationContext parent = wac.getParent();
assertNotNull(parent);
assertFalse(parent instanceof WebApplicationContext);
assertEquals("ear", ear);
assertEquals("root", root);
}
}

View File

@@ -33,21 +33,21 @@ import org.springframework.test.context.support.GenericPropertiesContextLoader;
/**
* <p>
* JUnit 4 based test class, which verifies the expected functionality of
* {@link SpringJUnit4ClassRunner} in conjunction with support for application
* contexts loaded from Java {@link Properties} files. Specifically, the
* {@link ContextConfiguration#loader() loaderClass} and
* {@link ContextConfiguration#resourceSuffix() resourceSuffix} attributes of
* &#064;ContextConfiguration are tested.
* {@link SpringJUnit4ClassRunner} in conjunction with support for application contexts
* loaded from Java {@link Properties} files. Specifically, the
* {@link ContextConfiguration#loader() loader} attribute of {@code ContextConfiguration}
* and the
* {@link org.springframework.test.context.support.GenericPropertiesContextLoader#getResourceSuffix()
* resourceSuffix} property of {@code GenericPropertiesContextLoader} are tested.
* </p>
* <p>
* Since no {@link ContextConfiguration#locations() locations} are explicitly
* defined, the {@link ContextConfiguration#resourceSuffix() resourceSuffix} is
* set to &quot;-context.properties&quot;, and
* {@link ContextConfiguration#generateDefaultLocations() generateDefaultLocations}
* is left set to its default value of {@code true}, this test class's
* dependencies will be injected via
* {@link Autowired annotation-based autowiring} from beans defined in the
* {@link ApplicationContext} loaded from the default classpath resource: &quot;{@code /org/springframework/test/junit4/PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests-context.properties}&quot;.
* Since no {@link ContextConfiguration#locations() locations} are explicitly defined, the
* {@code resourceSuffix} is set to &quot;-context.properties&quot;, and since default
* resource locations will be detected by default, this test class's dependencies will be
* injected via {@link Autowired annotation-based autowiring} from beans defined in the
* {@link ApplicationContext} loaded from the default classpath resource: &quot;
* {@code /org/springframework/test/junit4/PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests-context.properties}
* &quot;.
* </p>
*
* @author Sam Brannen

View File

@@ -38,6 +38,7 @@ import org.junit.runners.Suite.SuiteClasses;
* @author Sam Brannen
* @since 3.2
*/
@SuppressWarnings("javadoc")
@RunWith(Suite.class)
@SuiteClasses({ TestClass1.class, TestClass2.class })
public class Spr8849Tests {

View File

@@ -43,10 +43,11 @@ import org.testng.annotations.Test;
/**
* Integration tests that verify support for
* {@link import org.springframework.context.annotation.Configuration @Configuration}
* classes with TestNG-based tests.
* {@link org.springframework.context.annotation.Configuration @Configuration} classes
* with TestNG-based tests.
*
* <p>Configuration will be loaded from
* <p>
* Configuration will be loaded from
* {@link AnnotationConfigTransactionalTestNGSpringContextTests.ContextConfiguration}.
*
* @author Sam Brannen

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bar" class="java.lang.String" c:_="bar" />
<bean id="baz" class="java.lang.String" c:_="baz-child" />
</beans>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="foo" class="java.lang.String" c:_="foo-level-2" />
<bean id="baz" class="java.lang.String" c:_="baz" />
</beans>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dispatcher" class="java.lang.String" c:_="dispatcher" />
</beans>