Merge from sbrannen/SPR-9955
* SPR-9955: Introduce context bootstrap strategy in the TCF
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -50,18 +50,16 @@ public @interface ActiveProfiles {
|
||||
/**
|
||||
* Alias for {@link #profiles}.
|
||||
*
|
||||
* <p>This attribute may <strong>not</strong> be used in conjunction
|
||||
* with {@link #profiles} or {@link #resolver}, but it may be used
|
||||
* <em>instead</em> of them.
|
||||
* <p>This attribute may <strong>not</strong> be used in conjunction with
|
||||
* {@link #profiles}, but it may be used <em>instead</em> of {@link #profiles}.
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* The bean definition profiles to activate.
|
||||
*
|
||||
* <p>This attribute may <strong>not</strong> be used in conjunction
|
||||
* with {@link #value} or {@link #resolver}, but it may be used
|
||||
* <em>instead</em> of them.
|
||||
* <p>This attribute may <strong>not</strong> be used in conjunction with
|
||||
* {@link #value}, but it may be used <em>instead</em> of {@link #value}.
|
||||
*/
|
||||
String[] profiles() default {};
|
||||
|
||||
@@ -69,10 +67,6 @@ public @interface ActiveProfiles {
|
||||
* The type of {@link ActiveProfilesResolver} to use for resolving the active
|
||||
* bean definition profiles programmatically.
|
||||
*
|
||||
* <p>This attribute may <strong>not</strong> be used in conjunction
|
||||
* with {@link #profiles} or {@link #value}, but it may be used <em>instead</em>
|
||||
* of them in order to resolve the active profiles programmatically.
|
||||
*
|
||||
* @since 4.0
|
||||
* @see ActiveProfilesResolver
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* {@code BootstrapContext} encapsulates the context in which the <em>Spring
|
||||
* TestContext Framework</em> is bootstrapped.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
* @see BootstrapWith
|
||||
* @see TestContextBootstrapper
|
||||
*/
|
||||
public interface BootstrapContext {
|
||||
|
||||
/**
|
||||
* Get the {@link Class test class} for this bootstrap context.
|
||||
* @return the test class (never {@code null})
|
||||
*/
|
||||
Class<?> getTestClass();
|
||||
|
||||
/**
|
||||
* Get the {@link CacheAwareContextLoaderDelegate} to use for transparent
|
||||
* interaction with the <em>context cache</em>.
|
||||
*/
|
||||
CacheAwareContextLoaderDelegate getCacheAwareContextLoaderDelegate();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.util.ClassUtils;
|
||||
|
||||
import static org.springframework.beans.BeanUtils.*;
|
||||
import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
|
||||
/**
|
||||
* {@code BootstrapUtils} is a collection of utility methods to assist with
|
||||
* bootstrapping the <em>Spring TestContext Framework</em>.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
* @see BootstrapWith
|
||||
* @see BootstrapContext
|
||||
* @see TestContextBootstrapper
|
||||
*/
|
||||
abstract class BootstrapUtils {
|
||||
|
||||
private static final String DEFAULT_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME = "org.springframework.test.context.support.DefaultTestContextBootstrapper";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(BootstrapUtils.class);
|
||||
|
||||
|
||||
private BootstrapUtils() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the {@link TestContextBootstrapper} type for the test class in the
|
||||
* supplied {@link BootstrapContext}, instantiate it, and provide it a reference
|
||||
* to the {@link BootstrapContext}.
|
||||
*
|
||||
* <p>If the {@link BootstrapWith @BootstrapWith} annotation is present on
|
||||
* the test class, either directly or as a meta-annotation, then its
|
||||
* {@link BootstrapWith#value value} will be used as the bootstrapper type.
|
||||
* Otherwise, the {@link org.springframework.test.context.support.DefaultTestContextBootstrapper
|
||||
* DefaultTestContextBootstrapper} will be used.
|
||||
*
|
||||
* @param bootstrapContext the bootstrap context to use
|
||||
* @return a fully configured {@code TestContextBootstrapper}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static TestContextBootstrapper resolveTestContextBootstrapper(BootstrapContext bootstrapContext) {
|
||||
Class<?> testClass = bootstrapContext.getTestClass();
|
||||
|
||||
Class<? extends TestContextBootstrapper> clazz = null;
|
||||
try {
|
||||
BootstrapWith bootstrapWith = findAnnotation(testClass, BootstrapWith.class);
|
||||
if (bootstrapWith != null && !TestContextBootstrapper.class.equals(bootstrapWith.value())) {
|
||||
clazz = bootstrapWith.value();
|
||||
}
|
||||
else {
|
||||
clazz = (Class<? extends TestContextBootstrapper>) ClassUtils.forName(
|
||||
DEFAULT_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME, BootstrapUtils.class.getClassLoader());
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Instantiating TestContextBootstrapper from class [%s]", clazz.getName()));
|
||||
}
|
||||
|
||||
TestContextBootstrapper testContextBootstrapper = instantiateClass(clazz, TestContextBootstrapper.class);
|
||||
testContextBootstrapper.setBootstrapContext(bootstrapContext);
|
||||
|
||||
return testContextBootstrapper;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw new IllegalStateException("Could not load TestContextBootstrapper [" + clazz
|
||||
+ "]. Specify @BootstrapWith's 'value' attribute "
|
||||
+ "or make the default bootstrapper class available.", t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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 @BootstrapWith} defines class-level metadata that is used to determine
|
||||
* how to bootstrap the <em>Spring TestContext Framework</em>.
|
||||
*
|
||||
* <p>This annotation may also be used as a <em>meta-annotation</em> to create
|
||||
* custom <em>composed annotations</em>.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
* @see BootstrapContext
|
||||
* @see TestContextBootstrapper
|
||||
*/
|
||||
@Documented
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface BootstrapWith {
|
||||
|
||||
/**
|
||||
* The {@link TestContextBootstrapper} to use to bootstrap the <em>Spring
|
||||
* TestContext Framework</em>.
|
||||
*/
|
||||
Class<? extends TestContextBootstrapper> value() default TestContextBootstrapper.class;
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,97 +16,61 @@
|
||||
|
||||
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;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
|
||||
|
||||
/**
|
||||
* {@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.
|
||||
* A {@code CacheAwareContextLoaderDelegate} is responsible for {@linkplain
|
||||
* #loadContext loading} and {@linkplain #closeContext closing} application
|
||||
* contexts, interacting transparently with a <em>context cache</em> behind
|
||||
* the scenes.
|
||||
*
|
||||
* <p>Note: {@code CacheAwareContextLoaderDelegate} does not implement the
|
||||
* <p>Note: {@code CacheAwareContextLoaderDelegate} does not extend 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;
|
||||
}
|
||||
public interface CacheAwareContextLoaderDelegate {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Load the {@linkplain ApplicationContext application context} for the supplied
|
||||
* {@link MergedContextConfiguration} by delegating to the {@link ContextLoader}
|
||||
* configured in the given {@code MergedContextConfiguration}.
|
||||
*
|
||||
* <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.
|
||||
* <p>If the context is present in the <em>context cache</em> it will simply
|
||||
* be returned; otherwise, it will be loaded, stored in the cache, and returned.
|
||||
*
|
||||
* @param mergedContextConfiguration the merged context configuration to use
|
||||
* to load the application context; never {@code null}
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
ApplicationContext loadContext(MergedContextConfiguration mergedContextConfiguration);
|
||||
|
||||
/**
|
||||
* Remove the {@linkplain ApplicationContext application context} for the
|
||||
* supplied {@link MergedContextConfiguration} from the <em>context cache</em>
|
||||
* and {@linkplain ConfigurableApplicationContext#close() close} it if it is
|
||||
* an instance of {@link ConfigurableApplicationContext}.
|
||||
*
|
||||
* <p>The semantics of the supplied {@code HierarchyMode} must be honored when
|
||||
* removing the context from the cache. See the Javadoc for {@link HierarchyMode}
|
||||
* for details.
|
||||
*
|
||||
* <p>Generally speaking, this method should only be called if the state of
|
||||
* a singleton bean has been changed (potentially affecting future interaction
|
||||
* with the context) or if the context needs to be prematurely removed from
|
||||
* the cache.
|
||||
*
|
||||
* @param mergedContextConfiguration the merged context configuration for the
|
||||
* application context to close; never {@code null}
|
||||
* @param hierarchyMode the hierarchy mode; may be {@code null} if the context
|
||||
* is not part of a hierarchy
|
||||
* @since 4.1
|
||||
*/
|
||||
void closeContext(MergedContextConfiguration mergedContextConfiguration, HierarchyMode hierarchyMode);
|
||||
|
||||
}
|
||||
|
||||
@@ -91,6 +91,7 @@ public @interface ContextConfiguration {
|
||||
*
|
||||
* <p>This attribute may <strong>not</strong> be used in conjunction with
|
||||
* {@link #locations}, but it may be used instead of {@link #locations}.
|
||||
*
|
||||
* @since 3.0
|
||||
* @see #inheritLocations
|
||||
*/
|
||||
@@ -120,6 +121,7 @@ public @interface ContextConfiguration {
|
||||
*
|
||||
* <p>This attribute may <strong>not</strong> be used in conjunction with
|
||||
* {@link #value}, but it may be used instead of {@link #value}.
|
||||
*
|
||||
* @since 2.5
|
||||
* @see #inheritLocations
|
||||
*/
|
||||
|
||||
@@ -1,807 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.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.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.test.context.MetaAnnotationUtils.AnnotationDescriptor;
|
||||
import org.springframework.test.context.MetaAnnotationUtils.UntypedAnnotationDescriptor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.beans.BeanUtils.*;
|
||||
import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
import static org.springframework.test.context.MetaAnnotationUtils.*;
|
||||
|
||||
/**
|
||||
* Utility methods for working with {@link ContextLoader ContextLoaders} and
|
||||
* {@link SmartContextLoader SmartContextLoaders} and resolving resource locations,
|
||||
* annotated classes, active bean definition profiles, and application context
|
||||
* initializers.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Michail Nikolaev
|
||||
* @since 3.1
|
||||
* @see ContextLoader
|
||||
* @see SmartContextLoader
|
||||
* @see ContextConfiguration
|
||||
* @see ContextConfigurationAttributes
|
||||
* @see ActiveProfiles
|
||||
* @see ActiveProfilesResolver
|
||||
* @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";
|
||||
private static final String DEFAULT_WEB_CONTEXT_LOADER_CLASS_NAME = "org.springframework.test.context.web.WebDelegatingSmartContextLoader";
|
||||
|
||||
private static final String WEB_APP_CONFIGURATION_CLASS_NAME = "org.springframework.test.context.web.WebAppConfiguration";
|
||||
private static final String WEB_MERGED_CONTEXT_CONFIGURATION_CLASS_NAME = "org.springframework.test.context.web.WebMergedContextConfiguration";
|
||||
|
||||
|
||||
private ContextLoaderUtils() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@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>
|
||||
* (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})
|
||||
* @see #resolveContextLoaderClass
|
||||
*/
|
||||
static ContextLoader resolveContextLoader(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributesList, String defaultContextLoaderClassName) {
|
||||
Assert.notNull(testClass, "Class must not be null");
|
||||
Assert.notEmpty(configAttributesList, "ContextConfigurationAttributes list must not be empty");
|
||||
|
||||
if (!StringUtils.hasText(defaultContextLoaderClassName)) {
|
||||
Class<? extends Annotation> webAppConfigClass = loadWebAppConfigurationClass();
|
||||
defaultContextLoaderClassName = webAppConfigClass != null
|
||||
&& findAnnotation(testClass, webAppConfigClass) != null ? DEFAULT_WEB_CONTEXT_LOADER_CLASS_NAME
|
||||
: DEFAULT_CONTEXT_LOADER_CLASS_NAME;
|
||||
}
|
||||
|
||||
Class<? extends ContextLoader> contextLoaderClass = resolveContextLoaderClass(testClass, configAttributesList,
|
||||
defaultContextLoaderClassName);
|
||||
|
||||
return instantiateClass(contextLoaderClass, ContextLoader.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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:
|
||||
*
|
||||
* <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>
|
||||
* </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>
|
||||
* (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
|
||||
* @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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static Class<? extends ContextLoader> resolveContextLoaderClass(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributesList, String defaultContextLoaderClassName) {
|
||||
Assert.notNull(testClass, "Class must not be null");
|
||||
Assert.notEmpty(configAttributesList, "ContextConfigurationAttributes list must not be empty");
|
||||
Assert.hasText(defaultContextLoaderClassName, "Default ContextLoader class name must not be null or empty");
|
||||
|
||||
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Processing ContextLoader for context configuration attributes %s",
|
||||
configAttributes));
|
||||
}
|
||||
|
||||
Class<? extends ContextLoader> contextLoaderClass = configAttributes.getContextLoaderClass();
|
||||
if (!ContextLoader.class.equals(contextLoaderClass)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Found explicit ContextLoader class [%s] for context configuration attributes %s",
|
||||
contextLoaderClass.getName(), configAttributes));
|
||||
}
|
||||
return contextLoaderClass;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Using default ContextLoader class [%s] for test class [%s]",
|
||||
defaultContextLoaderClassName, testClass.getName()));
|
||||
}
|
||||
return (Class<? extends ContextLoader>) ClassUtils.forName(defaultContextLoaderClassName,
|
||||
ContextLoaderUtils.class.getClassLoader());
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw new IllegalStateException("Could not load default ContextLoader class ["
|
||||
+ defaultContextLoaderClassName + "]. Specify @ContextConfiguration's 'loader' "
|
||||
+ "attribute or make the default loader class available.", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for creating a {@link ContextConfigurationAttributes}
|
||||
* instance from the supplied {@link ContextConfiguration} annotation 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for creating a {@link ContextConfigurationAttributes}
|
||||
* instance from the supplied {@link AnnotationAttributes} and declaring
|
||||
* class and then adding the attributes to the supplied list.
|
||||
* @since 4.0
|
||||
*/
|
||||
private static void convertAnnotationAttributesToConfigAttributesAndAddToList(AnnotationAttributes annAttrs,
|
||||
Class<?> declaringClass, final List<ContextConfigurationAttributes> attributesList) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Retrieved @ContextConfiguration attributes [%s] for declaring class [%s].",
|
||||
annAttrs, declaringClass.getName()));
|
||||
}
|
||||
|
||||
ContextConfigurationAttributes attributes = new ContextConfigurationAttributes(declaringClass, annAttrs);
|
||||
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>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; or if a test class or composed annotation
|
||||
* in the class hierarchy declares both {@code @ContextConfiguration} and
|
||||
* {@code @ContextHierarchy} as top-level annotations.
|
||||
* @throws IllegalStateException if no class in the class hierarchy declares
|
||||
* {@code @ContextHierarchy}.
|
||||
*
|
||||
* @since 3.2.2
|
||||
* @see #buildContextHierarchyMap(Class)
|
||||
* @see #resolveContextConfigurationAttributes(Class)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static List<List<ContextConfigurationAttributes>> resolveContextHierarchyAttributes(Class<?> testClass) {
|
||||
Assert.notNull(testClass, "Class must not be null");
|
||||
Assert.state(findAnnotation(testClass, ContextHierarchy.class) != null, "@ContextHierarchy must be present");
|
||||
|
||||
final Class<ContextConfiguration> contextConfigType = ContextConfiguration.class;
|
||||
final Class<ContextHierarchy> contextHierarchyType = ContextHierarchy.class;
|
||||
final List<List<ContextConfigurationAttributes>> hierarchyAttributes = new ArrayList<List<ContextConfigurationAttributes>>();
|
||||
|
||||
UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(testClass, contextConfigType,
|
||||
contextHierarchyType);
|
||||
Assert.notNull(descriptor, 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 (descriptor != null) {
|
||||
Class<?> rootDeclaringClass = descriptor.getRootDeclaringClass();
|
||||
Class<?> declaringClass = descriptor.getDeclaringClass();
|
||||
|
||||
boolean contextConfigDeclaredLocally = isAnnotationDeclaredLocally(contextConfigType, declaringClass);
|
||||
boolean contextHierarchyDeclaredLocally = isAnnotationDeclaredLocally(contextHierarchyType, declaringClass);
|
||||
|
||||
if (contextConfigDeclaredLocally && contextHierarchyDeclaredLocally) {
|
||||
String msg = String.format("Class [%s] has been configured with both @ContextConfiguration "
|
||||
+ "and @ContextHierarchy. Only one of these annotations may be declared on a test class "
|
||||
+ "or composed annotation.", declaringClass.getName());
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
final List<ContextConfigurationAttributes> configAttributesList = new ArrayList<ContextConfigurationAttributes>();
|
||||
|
||||
if (contextConfigDeclaredLocally) {
|
||||
convertAnnotationAttributesToConfigAttributesAndAddToList(descriptor.getAnnotationAttributes(),
|
||||
rootDeclaringClass, configAttributesList);
|
||||
}
|
||||
else if (contextHierarchyDeclaredLocally) {
|
||||
ContextHierarchy contextHierarchy = getAnnotation(declaringClass, contextHierarchyType);
|
||||
for (ContextConfiguration contextConfiguration : contextHierarchy.value()) {
|
||||
convertContextConfigToConfigAttributesAndAddToList(contextConfiguration, rootDeclaringClass,
|
||||
configAttributesList);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This should theoretically never happen...
|
||||
String msg = String.format("Test class [%s] has been configured with neither @ContextConfiguration "
|
||||
+ "nor @ContextHierarchy as a class-level annotation.", rootDeclaringClass.getName());
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
hierarchyAttributes.add(0, configAttributesList);
|
||||
|
||||
descriptor = findAnnotationDescriptorForTypes(rootDeclaringClass.getSuperclass(), contextConfigType,
|
||||
contextHierarchyType);
|
||||
}
|
||||
|
||||
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}
|
||||
* @throws IllegalArgumentException if the lists of context configuration
|
||||
* attributes for each level in the {@code @ContextHierarchy} do not define
|
||||
* unique context configuration within the overall hierarchy.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for uniqueness
|
||||
Set<List<ContextConfigurationAttributes>> set = new HashSet<List<ContextConfigurationAttributes>>(map.values());
|
||||
if (set.size() != map.size()) {
|
||||
String msg = String.format("The @ContextConfiguration elements configured via "
|
||||
+ "@ContextHierarchy in test class [%s] and its superclasses must "
|
||||
+ "define unique contexts per hierarchy level.", testClass.getName());
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
static List<ContextConfigurationAttributes> resolveContextConfigurationAttributes(Class<?> testClass) {
|
||||
Assert.notNull(testClass, "Class must not be null");
|
||||
|
||||
final List<ContextConfigurationAttributes> attributesList = new ArrayList<ContextConfigurationAttributes>();
|
||||
|
||||
Class<ContextConfiguration> annotationType = ContextConfiguration.class;
|
||||
|
||||
AnnotationDescriptor<ContextConfiguration> descriptor = findAnnotationDescriptor(testClass, annotationType);
|
||||
Assert.notNull(descriptor, String.format(
|
||||
"Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]",
|
||||
annotationType.getName(), testClass.getName()));
|
||||
|
||||
while (descriptor != null) {
|
||||
convertAnnotationAttributesToConfigAttributesAndAddToList(descriptor.getAnnotationAttributes(),
|
||||
descriptor.getRootDeclaringClass(), attributesList);
|
||||
descriptor = findAnnotationDescriptor(descriptor.getRootDeclaringClass().getSuperclass(), annotationType);
|
||||
}
|
||||
|
||||
return attributesList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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})
|
||||
* @since 3.2
|
||||
*/
|
||||
static Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> resolveInitializerClasses(
|
||||
List<ContextConfigurationAttributes> configAttributesList) {
|
||||
Assert.notEmpty(configAttributesList, "ContextConfigurationAttributes list must not be empty");
|
||||
|
||||
final Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses = //
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
|
||||
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Processing context initializers for context configuration attributes %s",
|
||||
configAttributes));
|
||||
}
|
||||
|
||||
initializerClasses.addAll(Arrays.asList(configAttributes.getInitializers()));
|
||||
|
||||
if (!configAttributes.isInheritInitializers()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return initializerClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve <em>active bean definition profiles</em> for the supplied {@link Class}.
|
||||
*
|
||||
* <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})
|
||||
* @see ActiveProfiles
|
||||
* @see org.springframework.context.annotation.Profile
|
||||
*/
|
||||
static String[] resolveActiveProfiles(Class<?> testClass) {
|
||||
Assert.notNull(testClass, "Class must not be null");
|
||||
|
||||
Class<ActiveProfiles> annotationType = ActiveProfiles.class;
|
||||
|
||||
AnnotationDescriptor<ActiveProfiles> descriptor = findAnnotationDescriptor(testClass, annotationType);
|
||||
|
||||
if (descriptor == null && logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]",
|
||||
annotationType.getName(), testClass.getName()));
|
||||
}
|
||||
|
||||
final Set<String> activeProfiles = new HashSet<String>();
|
||||
|
||||
while (descriptor != null) {
|
||||
Class<?> rootDeclaringClass = descriptor.getRootDeclaringClass();
|
||||
Class<?> declaringClass = descriptor.getDeclaringClass();
|
||||
|
||||
AnnotationAttributes annAttrs = descriptor.getAnnotationAttributes();
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Retrieved @ActiveProfiles attributes [%s] for declaring class [%s].",
|
||||
annAttrs, declaringClass.getName()));
|
||||
}
|
||||
validateActiveProfilesConfiguration(declaringClass, annAttrs);
|
||||
|
||||
String[] profiles = annAttrs.getStringArray("profiles");
|
||||
String[] valueProfiles = annAttrs.getStringArray("value");
|
||||
Class<? extends ActiveProfilesResolver> resolverClass = annAttrs.getClass("resolver");
|
||||
|
||||
boolean resolverDeclared = !ActiveProfilesResolver.class.equals(resolverClass);
|
||||
boolean valueDeclared = !ObjectUtils.isEmpty(valueProfiles);
|
||||
|
||||
if (resolverDeclared) {
|
||||
ActiveProfilesResolver resolver = null;
|
||||
try {
|
||||
resolver = instantiateClass(resolverClass, ActiveProfilesResolver.class);
|
||||
}
|
||||
catch (Exception e) {
|
||||
String msg = String.format("Could not instantiate ActiveProfilesResolver of "
|
||||
+ "type [%s] for test class [%s].", resolverClass.getName(), rootDeclaringClass.getName());
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg, e);
|
||||
}
|
||||
|
||||
profiles = resolver.resolve(rootDeclaringClass);
|
||||
if (profiles == null) {
|
||||
String msg = String.format(
|
||||
"ActiveProfilesResolver [%s] returned a null array of bean definition profiles.",
|
||||
resolverClass.getName());
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
}
|
||||
else if (valueDeclared) {
|
||||
profiles = valueProfiles;
|
||||
}
|
||||
|
||||
for (String profile : profiles) {
|
||||
if (StringUtils.hasText(profile)) {
|
||||
activeProfiles.add(profile.trim());
|
||||
}
|
||||
}
|
||||
|
||||
descriptor = annAttrs.getBoolean("inheritProfiles") ? findAnnotationDescriptor(
|
||||
rootDeclaringClass.getSuperclass(), annotationType) : null;
|
||||
}
|
||||
|
||||
return StringUtils.toStringArray(activeProfiles);
|
||||
}
|
||||
|
||||
private static void validateActiveProfilesConfiguration(Class<?> declaringClass, AnnotationAttributes annAttrs) {
|
||||
String[] valueProfiles = annAttrs.getStringArray("value");
|
||||
String[] profiles = annAttrs.getStringArray("profiles");
|
||||
Class<? extends ActiveProfilesResolver> resolverClass = annAttrs.getClass("resolver");
|
||||
boolean valueDeclared = !ObjectUtils.isEmpty(valueProfiles);
|
||||
boolean profilesDeclared = !ObjectUtils.isEmpty(profiles);
|
||||
boolean resolverDeclared = !ActiveProfilesResolver.class.equals(resolverClass);
|
||||
|
||||
String msg = null;
|
||||
|
||||
if (valueDeclared && profilesDeclared) {
|
||||
msg = String.format("Test class [%s] has been configured with @ActiveProfiles' 'value' [%s] "
|
||||
+ "and 'profiles' [%s] attributes. Only one declaration of active bean "
|
||||
+ "definition profiles is permitted per @ActiveProfiles annotation.", declaringClass.getName(),
|
||||
ObjectUtils.nullSafeToString(valueProfiles), ObjectUtils.nullSafeToString(profiles));
|
||||
}
|
||||
else if (valueDeclared && resolverDeclared) {
|
||||
msg = String.format("Test class [%s] has been configured with @ActiveProfiles' 'value' [%s] "
|
||||
+ "and 'resolver' [%s] attributes. Only one source of active bean "
|
||||
+ "definition profiles is permitted per @ActiveProfiles annotation, "
|
||||
+ "either declaritively or programmatically.", declaringClass.getName(),
|
||||
ObjectUtils.nullSafeToString(valueProfiles), resolverClass.getName());
|
||||
}
|
||||
else if (profilesDeclared && resolverDeclared) {
|
||||
msg = String.format("Test class [%s] has been configured with @ActiveProfiles' 'profiles' [%s] "
|
||||
+ "and 'resolver' [%s] attributes. Only one source of active bean "
|
||||
+ "definition profiles is permitted per @ActiveProfiles annotation, "
|
||||
+ "either declaritively or programmatically.", declaringClass.getName(),
|
||||
ObjectUtils.nullSafeToString(profiles), resolverClass.getName());
|
||||
}
|
||||
|
||||
if (msg != null) {
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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({ "unchecked" })
|
||||
static MergedContextConfiguration buildMergedContextConfiguration(Class<?> testClass,
|
||||
String defaultContextLoaderClassName, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
|
||||
|
||||
if (findAnnotationDescriptorForTypes(testClass, ContextConfiguration.class, ContextHierarchy.class) == null) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format(
|
||||
"Neither @ContextConfiguration nor @ContextHierarchy found for test class [%s]",
|
||||
testClass.getName()));
|
||||
}
|
||||
return new MergedContextConfiguration(testClass, null, null, null, null);
|
||||
}
|
||||
|
||||
if (findAnnotation(testClass, ContextHierarchy.class) != null) {
|
||||
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
|
||||
* @see SmartContextLoader#processContextConfiguration
|
||||
* @see ContextLoader#processLocations
|
||||
* @see #resolveActiveProfiles
|
||||
* @see MergedContextConfiguration
|
||||
*/
|
||||
private static MergedContextConfiguration buildMergedContextConfiguration(final Class<?> testClass,
|
||||
final List<ContextConfigurationAttributes> configAttributesList,
|
||||
final String defaultContextLoaderClassName, MergedContextConfiguration parentConfig,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
|
||||
|
||||
final ContextLoader contextLoader = resolveContextLoader(testClass, configAttributesList,
|
||||
defaultContextLoaderClassName);
|
||||
final List<String> locationsList = new ArrayList<String>();
|
||||
final List<Class<?>> classesList = new ArrayList<Class<?>>();
|
||||
|
||||
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Processing locations and classes for context configuration attributes %s",
|
||||
configAttributes));
|
||||
}
|
||||
|
||||
if (contextLoader instanceof SmartContextLoader) {
|
||||
SmartContextLoader smartContextLoader = (SmartContextLoader) contextLoader;
|
||||
smartContextLoader.processContextConfiguration(configAttributes);
|
||||
locationsList.addAll(0, Arrays.asList(configAttributes.getLocations()));
|
||||
classesList.addAll(0, Arrays.asList(configAttributes.getClasses()));
|
||||
}
|
||||
else {
|
||||
String[] processedLocations = contextLoader.processLocations(configAttributes.getDeclaringClass(),
|
||||
configAttributes.getLocations());
|
||||
locationsList.addAll(0, Arrays.asList(processedLocations));
|
||||
// Legacy ContextLoaders don't know how to process classes
|
||||
}
|
||||
|
||||
if (!configAttributes.isInheritLocations()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String[] locations = StringUtils.toStringArray(locationsList);
|
||||
Class<?>[] classes = ClassUtils.toClassArray(classesList);
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses = resolveInitializerClasses(configAttributesList);
|
||||
String[] activeProfiles = resolveActiveProfiles(testClass);
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildWebMergedContextConfiguration(testClass, locations, classes,
|
||||
initializerClasses, activeProfiles, contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
|
||||
|
||||
if (mergedConfig == null) {
|
||||
mergedConfig = new MergedContextConfiguration(testClass, locations, classes, initializerClasses,
|
||||
activeProfiles, contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
|
||||
}
|
||||
|
||||
return mergedConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @since 3.2
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Class<? extends Annotation> loadWebAppConfigurationClass() {
|
||||
Class<? extends Annotation> webAppConfigClass = null;
|
||||
try {
|
||||
webAppConfigClass = (Class<? extends Annotation>) ClassUtils.forName(WEB_APP_CONFIGURATION_CLASS_NAME,
|
||||
ContextLoaderUtils.class.getClassLoader());
|
||||
}
|
||||
catch (Throwable t) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not load @WebAppConfiguration class [" + WEB_APP_CONFIGURATION_CLASS_NAME + "].", t);
|
||||
}
|
||||
}
|
||||
return webAppConfigClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @since 3.2
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static MergedContextConfiguration buildWebMergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
String[] locations,
|
||||
Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses,
|
||||
String[] activeProfiles, ContextLoader contextLoader,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parentConfig) {
|
||||
|
||||
Class<? extends Annotation> webAppConfigClass = loadWebAppConfigurationClass();
|
||||
if (webAppConfigClass != null) {
|
||||
|
||||
Annotation annotation = findAnnotation(testClass, webAppConfigClass);
|
||||
if (annotation != null) {
|
||||
|
||||
String resourceBasePath = (String) AnnotationUtils.getValue(annotation);
|
||||
|
||||
try {
|
||||
Class<? extends MergedContextConfiguration> webMergedConfigClass = (Class<? extends MergedContextConfiguration>) ClassUtils.forName(
|
||||
WEB_MERGED_CONTEXT_CONFIGURATION_CLASS_NAME, ContextLoaderUtils.class.getClassLoader());
|
||||
|
||||
Constructor<? extends MergedContextConfiguration> constructor = ClassUtils.getConstructorIfAvailable(
|
||||
webMergedConfigClass, Class.class, String[].class, Class[].class, Set.class, String[].class,
|
||||
String.class, ContextLoader.class, CacheAwareContextLoaderDelegate.class,
|
||||
MergedContextConfiguration.class);
|
||||
|
||||
if (constructor != null) {
|
||||
return instantiateClass(constructor, testClass, locations, classes, initializerClasses,
|
||||
activeProfiles, resourceBasePath, contextLoader, cacheAwareContextLoaderDelegate,
|
||||
parentConfig);
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not instantiate [" + WEB_MERGED_CONTEXT_CONFIGURATION_CLASS_NAME + "].", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link BootstrapContext} interface.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
*/
|
||||
class DefaultBootstrapContext implements BootstrapContext {
|
||||
|
||||
private final Class<?> testClass;
|
||||
private final CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate;
|
||||
|
||||
|
||||
DefaultBootstrapContext(Class<?> testClass, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
|
||||
Assert.notNull(testClass, "Test class must not be null");
|
||||
Assert.notNull(cacheAwareContextLoaderDelegate, "CacheAwareContextLoaderDelegate must not be null");
|
||||
this.testClass = testClass;
|
||||
this.cacheAwareContextLoaderDelegate = cacheAwareContextLoaderDelegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getTestClass() {
|
||||
return this.testClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public CacheAwareContextLoaderDelegate getCacheAwareContextLoaderDelegate() {
|
||||
return this.cacheAwareContextLoaderDelegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a String representation of this bootstrap context's state.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this)//
|
||||
.append("testClass", testClass)//
|
||||
.append("cacheAwareContextLoaderDelegate", nullSafeToString(cacheAwareContextLoaderDelegate))//
|
||||
.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a null-safe {@link String} representation of the supplied
|
||||
* {@link CacheAwareContextLoaderDelegate} based solely on the fully qualified
|
||||
* name of the delegate or "null" if the supplied delegate is
|
||||
* {@code null}.
|
||||
*/
|
||||
private static String nullSafeToString(CacheAwareContextLoaderDelegate delegate) {
|
||||
return delegate == null ? "null" : delegate.getClass().getName();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.test.annotation.DirtiesContext.HierarchyMode;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link CacheAwareContextLoaderDelegate} interface.
|
||||
*
|
||||
* <p>Although {@code DefaultCacheAwareContextLoaderDelegate} was first introduced
|
||||
* in Spring Framework 4.1, the initial implementation of this class was extracted
|
||||
* from the existing code base for {@code CacheAwareContextLoaderDelegate} when
|
||||
* {@code CacheAwareContextLoaderDelegate} was converted into an interface.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
*/
|
||||
class DefaultCacheAwareContextLoaderDelegate implements CacheAwareContextLoaderDelegate {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DefaultCacheAwareContextLoaderDelegate.class);
|
||||
|
||||
private final ContextCache contextCache;
|
||||
|
||||
|
||||
DefaultCacheAwareContextLoaderDelegate(ContextCache contextCache) {
|
||||
Assert.notNull(contextCache, "ContextCache must not be null");
|
||||
this.contextCache = contextCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the {@code ApplicationContext} for the supplied merged context configuration.
|
||||
* <p>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;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void closeContext(MergedContextConfiguration mergedContextConfiguration, HierarchyMode hierarchyMode) {
|
||||
contextCache.remove(mergedContextConfiguration, hierarchyMode);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -40,8 +40,6 @@ class DefaultTestContext extends AttributeAccessorSupport implements TestContext
|
||||
|
||||
private static final long serialVersionUID = -5827157174866681233L;
|
||||
|
||||
private final ContextCache contextCache;
|
||||
|
||||
private final CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate;
|
||||
|
||||
private final MergedContextConfiguration mergedContextConfiguration;
|
||||
@@ -56,42 +54,17 @@ class DefaultTestContext extends AttributeAccessorSupport implements TestContext
|
||||
|
||||
|
||||
/**
|
||||
* Delegates to {@link #DefaultTestContext(Class, ContextCache, String)} with a
|
||||
* value of {@code null} for the default {@code ContextLoader} class name.
|
||||
* Construct a new test context using the supplied {@link TestContextBootstrapper}.
|
||||
* @param testContextBootstrapper the {@code TestContextBootstrapper} to use
|
||||
* to construct the test context (must not be {@code null})
|
||||
*/
|
||||
DefaultTestContext(Class<?> testClass, ContextCache contextCache) {
|
||||
this(testClass, contextCache, null);
|
||||
}
|
||||
DefaultTestContext(TestContextBootstrapper testContextBootstrapper) {
|
||||
Assert.notNull(testContextBootstrapper, "TestContextBootstrapper must not be null");
|
||||
|
||||
/**
|
||||
* Construct a new test context for the supplied {@linkplain Class test class}
|
||||
* and {@linkplain ContextCache context cache} and parse the corresponding
|
||||
* {@link ContextConfiguration @ContextConfiguration} or
|
||||
* {@link ContextHierarchy @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 {@code @ContextConfiguration}, a
|
||||
* {@link org.springframework.test.context.support.DelegatingSmartContextLoader
|
||||
* 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
|
||||
* context should retrieve application contexts (must not be
|
||||
* {@code null})
|
||||
* @param defaultContextLoaderClassName the name of the default
|
||||
* {@code ContextLoader} class to use (may be {@code null})
|
||||
*/
|
||||
DefaultTestContext(Class<?> testClass, ContextCache contextCache, String defaultContextLoaderClassName) {
|
||||
Assert.notNull(testClass, "Test class must not be null");
|
||||
Assert.notNull(contextCache, "ContextCache must not be null");
|
||||
|
||||
this.testClass = testClass;
|
||||
this.contextCache = contextCache;
|
||||
this.cacheAwareContextLoaderDelegate = new CacheAwareContextLoaderDelegate(contextCache);
|
||||
this.mergedContextConfiguration = ContextLoaderUtils.buildMergedContextConfiguration(testClass,
|
||||
defaultContextLoaderClassName, cacheAwareContextLoaderDelegate);
|
||||
BootstrapContext bootstrapContext = testContextBootstrapper.getBootstrapContext();
|
||||
this.testClass = bootstrapContext.getTestClass();
|
||||
this.cacheAwareContextLoaderDelegate = bootstrapContext.getCacheAwareContextLoaderDelegate();
|
||||
this.mergedContextConfiguration = testContextBootstrapper.buildMergedContextConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,6 +74,13 @@ class DefaultTestContext extends AttributeAccessorSupport implements TestContext
|
||||
return cacheAwareContextLoaderDelegate.loadContext(mergedContextConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void markApplicationContextDirty(HierarchyMode hierarchyMode) {
|
||||
cacheAwareContextLoaderDelegate.closeContext(mergedContextConfiguration, hierarchyMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@@ -129,13 +109,6 @@ class DefaultTestContext extends AttributeAccessorSupport implements TestContext
|
||||
return testException;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void markApplicationContextDirty(HierarchyMode hierarchyMode) {
|
||||
contextCache.remove(mergedContextConfiguration, hierarchyMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.util.List;
|
||||
|
||||
/**
|
||||
* {@code TestContextBootstrapper} defines a strategy SPI for bootstrapping the
|
||||
* <em>Spring TestContext Framework</em>.
|
||||
*
|
||||
* <p>A custom bootstrapping strategy can be configured for a test class via
|
||||
* {@link BootstrapWith @BootstrapWith}, either directly or as a meta-annotation.
|
||||
* See {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration}
|
||||
* for an example.
|
||||
*
|
||||
* <p>The {@link TestContextManager} uses a {@code TestContextBootstrapper} to
|
||||
* {@linkplain #getTestExecutionListeners get the TestExecutionListeners} for the
|
||||
* current test and to {@linkplain #buildMergedContextConfiguration build the
|
||||
* merged context configuration} necessary to create the {@link TestContext} that
|
||||
* it manages.
|
||||
*
|
||||
* <p>Concrete implementations must provide a {@code public} no-args constructor.
|
||||
*
|
||||
* <p><strong>Note</strong>: this SPI might potentially change in the future in
|
||||
* order to accommodate new requirements. Implementers are therefore strongly encouraged
|
||||
* <em>not</em> to implement this interface directly but rather to <em>extend</em>
|
||||
* {@link org.springframework.test.context.support.AbstractTestContextBootstrapper
|
||||
* AbstractTestContextBootstrapper} or one of its concrete subclasses instead.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
* @see BootstrapWith
|
||||
* @see BootstrapContext
|
||||
*/
|
||||
public interface TestContextBootstrapper {
|
||||
|
||||
/**
|
||||
* Set the {@link BootstrapContext} to be used by this bootstrapper.
|
||||
*/
|
||||
void setBootstrapContext(BootstrapContext bootstrapContext);
|
||||
|
||||
/**
|
||||
* Get the {@link BootstrapContext} associated with this bootstrapper.
|
||||
*/
|
||||
BootstrapContext getBootstrapContext();
|
||||
|
||||
/**
|
||||
* Get a list of newly instantiated {@link TestExecutionListener TestExecutionListeners}
|
||||
* for the test class in the {@link BootstrapContext} associated with this bootstrapper.
|
||||
* <p>If {@link TestExecutionListeners @TestExecutionListeners} is not
|
||||
* <em>present</em> on the test class in the {@code BootstrapContext},
|
||||
* <em>default</em> listeners should be returned. Concrete implementations
|
||||
* are free to determine what comprises the set of default listeners.
|
||||
* <p>The {@link TestExecutionListeners#inheritListeners() inheritListeners}
|
||||
* flag of {@link TestExecutionListeners @TestExecutionListeners} must be
|
||||
* taken into consideration. Specifically, if the {@code inheritListeners}
|
||||
* flag is set to {@code true}, listeners declared for a given test class must
|
||||
* be appended to the end of the list of listeners declared in superclasses.
|
||||
* @return a list of {@code TestExecutionListener} instances
|
||||
*/
|
||||
List<TestExecutionListener> getTestExecutionListeners();
|
||||
|
||||
/**
|
||||
* Build the {@linkplain MergedContextConfiguration merged context configuration}
|
||||
* for the test class in the {@link BootstrapContext} associated with this
|
||||
* bootstrapper.
|
||||
* <p>Implementations must take the following into account when building the
|
||||
* merged configuration:
|
||||
* <ul>
|
||||
* <li>Context hierarchies declared via {@link ContextHierarchy @ContextHierarchy}
|
||||
* and {@link ContextConfiguration @ContextConfiguration}</li>
|
||||
* <li>Active bean definition profiles declared via {@link ActiveProfiles @ActiveProfiles}</li>
|
||||
* <li>{@linkplain org.springframework.context.ApplicationContextInitializer
|
||||
* Context initializers} declared via {@link ContextConfiguration#initializers}</li>
|
||||
* <p>Consult the Javadoc for the aforementioned annotations for details on
|
||||
* the required semantics.
|
||||
* <p>When determining which {@link ContextLoader} to use for a given test
|
||||
* class, the following algorithm should be used:
|
||||
* <ol>
|
||||
* <li>If a {@code ContextLoader} class has been explicitly declared via
|
||||
* {@link ContextConfiguration#loader}, use it.</li>
|
||||
* <li>Otherwise, if the name of a {@linkplain BootstrapContext#getCustomDefaultContextLoaderClassName
|
||||
* custom default ContextLoader class} has been provided in the {@link BootstrapContext},
|
||||
* use it.</li>
|
||||
* <li>Otherwise, concrete implementations are free to determine which
|
||||
* {@code ContextLoader} class to use as as default.</li>
|
||||
* @return the merged context configuration, never {@code null}
|
||||
*/
|
||||
MergedContextConfiguration buildMergedContextConfiguration();
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,119 +18,111 @@ package org.springframework.test.context;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.test.context.MetaAnnotationUtils.AnnotationDescriptor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import static org.springframework.test.context.MetaAnnotationUtils.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* {@code TestContextManager} is the main entry point into the
|
||||
* <em>Spring TestContext Framework</em>, which provides support for loading and
|
||||
* accessing {@link ApplicationContext application contexts}, dependency
|
||||
* injection of test instances,
|
||||
* {@link org.springframework.transaction.annotation.Transactional
|
||||
* transactional} execution of test methods, etc.
|
||||
* </p>
|
||||
* <p>
|
||||
* Specifically, a {@code TestContextManager} is responsible for managing a
|
||||
* {@code TestContextManager} is the main entry point into the <em>Spring
|
||||
* TestContext Framework</em>, which provides support for loading and accessing
|
||||
* {@link org.springframework.context.ApplicationContext application contexts},
|
||||
* dependency injection of test instances,
|
||||
* {@link org.springframework.transaction.annotation.Transactional transactional}
|
||||
* execution of test methods, etc.
|
||||
*
|
||||
* <p>Specifically, a {@code TestContextManager} is responsible for managing a
|
||||
* single {@link TestContext} and signaling events to all registered
|
||||
* {@link TestExecutionListener TestExecutionListeners} at well defined test
|
||||
* execution points:
|
||||
* </p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link #beforeTestClass() before test class execution}: prior to any
|
||||
* <em>before class methods</em> of a particular testing framework (e.g., JUnit
|
||||
* 4's {@link org.junit.BeforeClass @BeforeClass})</li>
|
||||
* 4's {@link org.junit.BeforeClass @BeforeClass})</li>
|
||||
* <li>{@link #prepareTestInstance(Object) test instance preparation}:
|
||||
* immediately following instantiation of the test instance</li>
|
||||
* <li>{@link #beforeTestMethod(Object, Method) before test method execution}:
|
||||
* prior to any <em>before methods</em> of a particular testing framework (e.g.,
|
||||
* JUnit 4's {@link org.junit.Before @Before})</li>
|
||||
* JUnit 4's {@link org.junit.Before @Before})</li>
|
||||
* <li>{@link #afterTestMethod(Object, Method, Throwable) after test method
|
||||
* execution}: after any <em>after methods</em> of a particular testing
|
||||
* framework (e.g., JUnit 4's {@link org.junit.After @After})</li>
|
||||
* framework (e.g., JUnit 4's {@link org.junit.After @After})</li>
|
||||
* <li>{@link #afterTestClass() after test class execution}: after any
|
||||
* <em>after class methods</em> of a particular testing framework (e.g., JUnit
|
||||
* 4's {@link org.junit.AfterClass @AfterClass})</li>
|
||||
* 4's {@link org.junit.AfterClass @AfterClass})</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see BootstrapWith
|
||||
* @see BootstrapContext
|
||||
* @see TestContextBootstrapper
|
||||
* @see TestContext
|
||||
* @see TestExecutionListener
|
||||
* @see TestExecutionListeners
|
||||
* @see ContextConfiguration
|
||||
* @see ContextHierarchy
|
||||
* @see org.springframework.test.context.transaction.TransactionConfiguration
|
||||
*/
|
||||
public class TestContextManager {
|
||||
|
||||
private static final String[] DEFAULT_TEST_EXECUTION_LISTENER_CLASS_NAMES = new String[] {
|
||||
"org.springframework.test.context.web.ServletTestExecutionListener",
|
||||
"org.springframework.test.context.support.DependencyInjectionTestExecutionListener",
|
||||
"org.springframework.test.context.support.DirtiesContextTestExecutionListener",
|
||||
"org.springframework.test.context.transaction.TransactionalTestExecutionListener" };
|
||||
|
||||
private static final Log logger = LogFactory.getLog(TestContextManager.class);
|
||||
|
||||
/**
|
||||
* Cache of Spring application contexts. This needs to be static, as tests
|
||||
* may be destroyed and recreated between running individual test methods,
|
||||
* for example with JUnit.
|
||||
* Cache of Spring application contexts.
|
||||
* <p>This needs to be static, since test instances may be destroyed and
|
||||
* recreated between invocations of individual test methods, as is the case
|
||||
* with JUnit.
|
||||
*/
|
||||
static final ContextCache contextCache = new ContextCache();
|
||||
|
||||
private final TestContext testContext;
|
||||
|
||||
private final TestContextBootstrapper testContextBootstrapper;
|
||||
|
||||
private final List<TestExecutionListener> testExecutionListeners = new ArrayList<TestExecutionListener>();
|
||||
|
||||
|
||||
/**
|
||||
* Delegates to {@link #TestContextManager(Class, String)} with a value of
|
||||
* {@code null} for the default {@code ContextLoader} class name.
|
||||
* Construct a new {@code TestContextManager} for the specified {@linkplain Class
|
||||
* test class} and automatically {@link #registerTestExecutionListeners register} the
|
||||
* {@link TestExecutionListener TestExecutionListeners} configured for the test class
|
||||
* via the {@link TestExecutionListeners @TestExecutionListeners} annotation.
|
||||
* @param testClass the test class to be managed
|
||||
* @see #registerTestExecutionListeners(List)
|
||||
*/
|
||||
public TestContextManager(Class<?> testClass) {
|
||||
this(testClass, null);
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = new DefaultCacheAwareContextLoaderDelegate(
|
||||
contextCache);
|
||||
BootstrapContext bootstrapContext = new DefaultBootstrapContext(testClass, cacheAwareContextLoaderDelegate);
|
||||
this.testContextBootstrapper = BootstrapUtils.resolveTestContextBootstrapper(bootstrapContext);
|
||||
this.testContext = new DefaultTestContext(testContextBootstrapper);
|
||||
registerTestExecutionListeners(testContextBootstrapper.getTestExecutionListeners());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @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})
|
||||
* @see #registerTestExecutionListeners(TestExecutionListener...)
|
||||
*/
|
||||
public TestContextManager(Class<?> testClass, String defaultContextLoaderClassName) {
|
||||
this.testContext = new DefaultTestContext(testClass, contextCache, defaultContextLoaderClassName);
|
||||
registerTestExecutionListeners(retrieveTestExecutionListeners(testClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link TestContext} managed by this
|
||||
* {@code TestContextManager}.
|
||||
* Get the {@link TestContext} managed by this {@code TestContextManager}.
|
||||
*/
|
||||
protected final TestContext getTestContext() {
|
||||
return this.testContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the supplied {@link TestExecutionListener TestExecutionListeners}
|
||||
* by appending them to the set of listeners used by this {@code TestContextManager}.
|
||||
* Register the supplied list of {@link TestExecutionListener TestExecutionListeners}
|
||||
* by appending them to the list of listeners used by this {@code TestContextManager}.
|
||||
* @see #registerTestExecutionListeners(TestExecutionListener...)
|
||||
*/
|
||||
public void registerTestExecutionListeners(List<TestExecutionListener> testExecutionListeners) {
|
||||
registerTestExecutionListeners(testExecutionListeners.toArray(new TestExecutionListener[testExecutionListeners.size()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the supplied array of {@link TestExecutionListener TestExecutionListeners}
|
||||
* by appending them to the list of listeners used by this {@code TestContextManager}.
|
||||
*/
|
||||
public void registerTestExecutionListeners(TestExecutionListener... testExecutionListeners) {
|
||||
for (TestExecutionListener listener : testExecutionListeners) {
|
||||
@@ -162,110 +154,11 @@ public class TestContextManager {
|
||||
return listenersReversed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an array of newly instantiated {@link TestExecutionListener TestExecutionListeners}
|
||||
* for the specified {@link Class class}. If {@link TestExecutionListeners @TestExecutionListeners}
|
||||
* is not <em>present</em> on the supplied class, the default listeners will be returned.
|
||||
* <p>Note that the {@link TestExecutionListeners#inheritListeners() inheritListeners} flag of
|
||||
* {@link TestExecutionListeners @TestExecutionListeners} will be taken into consideration.
|
||||
* Specifically, if the {@code inheritListeners} flag is set to {@code true}, listeners
|
||||
* defined in the annotated class will be appended to the listeners defined in superclasses.
|
||||
* @param clazz the test class for which the listeners should be retrieved
|
||||
* @return an array of TestExecutionListeners for the specified class
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private TestExecutionListener[] retrieveTestExecutionListeners(Class<?> clazz) {
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
Class<TestExecutionListeners> annotationType = TestExecutionListeners.class;
|
||||
List<Class<? extends TestExecutionListener>> classesList = new ArrayList<Class<? extends TestExecutionListener>>();
|
||||
|
||||
AnnotationDescriptor<TestExecutionListeners> descriptor = findAnnotationDescriptor(clazz, annotationType);
|
||||
|
||||
// Use defaults?
|
||||
if (descriptor == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("@TestExecutionListeners is not present for class [" + clazz + "]: using defaults.");
|
||||
}
|
||||
classesList.addAll(getDefaultTestExecutionListenerClasses());
|
||||
}
|
||||
else {
|
||||
// Traverse the class hierarchy...
|
||||
while (descriptor != null) {
|
||||
Class<?> declaringClass = descriptor.getDeclaringClass();
|
||||
|
||||
AnnotationAttributes annAttrs = descriptor.getAnnotationAttributes();
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format(
|
||||
"Retrieved @TestExecutionListeners attributes [%s] for declaring class [%s].", annAttrs,
|
||||
declaringClass));
|
||||
}
|
||||
|
||||
Class<? extends TestExecutionListener>[] valueListenerClasses = (Class<? extends TestExecutionListener>[]) annAttrs.getClassArray("value");
|
||||
Class<? extends TestExecutionListener>[] listenerClasses = (Class<? extends TestExecutionListener>[]) annAttrs.getClassArray("listeners");
|
||||
if (!ObjectUtils.isEmpty(valueListenerClasses) && !ObjectUtils.isEmpty(listenerClasses)) {
|
||||
String msg = String.format(
|
||||
"Class [%s] has been configured with @TestExecutionListeners' 'value' [%s] "
|
||||
+ "and 'listeners' [%s] attributes. Use one or the other, but not both.",
|
||||
declaringClass, ObjectUtils.nullSafeToString(valueListenerClasses),
|
||||
ObjectUtils.nullSafeToString(listenerClasses));
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
else if (!ObjectUtils.isEmpty(valueListenerClasses)) {
|
||||
listenerClasses = valueListenerClasses;
|
||||
}
|
||||
|
||||
if (listenerClasses != null) {
|
||||
classesList.addAll(0, Arrays.<Class<? extends TestExecutionListener>> asList(listenerClasses));
|
||||
}
|
||||
|
||||
descriptor = (annAttrs.getBoolean("inheritListeners") ? findAnnotationDescriptor(
|
||||
descriptor.getRootDeclaringClass().getSuperclass(), annotationType) : null);
|
||||
}
|
||||
}
|
||||
|
||||
List<TestExecutionListener> listeners = new ArrayList<TestExecutionListener>(classesList.size());
|
||||
for (Class<? extends TestExecutionListener> listenerClass : classesList) {
|
||||
try {
|
||||
listeners.add(BeanUtils.instantiateClass(listenerClass));
|
||||
}
|
||||
catch (NoClassDefFoundError err) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("Could not instantiate TestExecutionListener class [%s]. "
|
||||
+ "Specify custom listener classes or make the default listener classes "
|
||||
+ "(and their dependencies) available.", listenerClass.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return listeners.toArray(new TestExecutionListener[listeners.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the default {@link TestExecutionListener} classes.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Set<Class<? extends TestExecutionListener>> getDefaultTestExecutionListenerClasses() {
|
||||
Set<Class<? extends TestExecutionListener>> defaultListenerClasses = new LinkedHashSet<Class<? extends TestExecutionListener>>();
|
||||
for (String className : DEFAULT_TEST_EXECUTION_LISTENER_CLASS_NAMES) {
|
||||
try {
|
||||
defaultListenerClasses.add((Class<? extends TestExecutionListener>) getClass().getClassLoader().loadClass(
|
||||
className));
|
||||
}
|
||||
catch (Throwable t) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not load default TestExecutionListener class [" + className
|
||||
+ "]. Specify custom listener classes or make the default listener classes available.", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultListenerClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for pre-processing a test class <em>before</em> execution of any
|
||||
* tests within the class. Should be called prior to any framework-specific
|
||||
* <em>before class methods</em> (e.g., methods annotated with JUnit's
|
||||
* {@link org.junit.BeforeClass @BeforeClass}).
|
||||
* {@link org.junit.BeforeClass @BeforeClass}).
|
||||
* <p>An attempt will be made to give each registered
|
||||
* {@link TestExecutionListener} a chance to pre-process the test class
|
||||
* execution. If a listener throws an exception, however, the remaining
|
||||
@@ -331,7 +224,7 @@ public class TestContextManager {
|
||||
* {@link Method test method}, for example for setting up test fixtures,
|
||||
* starting a transaction, etc. Should be called prior to any
|
||||
* framework-specific <em>before methods</em> (e.g., methods annotated with
|
||||
* JUnit's {@link org.junit.Before @Before}).
|
||||
* JUnit's {@link org.junit.Before @Before}).
|
||||
* <p>The managed {@link TestContext} will be updated with the supplied
|
||||
* {@code testInstance} and {@code testMethod}.
|
||||
* <p>An attempt will be made to give each registered
|
||||
@@ -369,7 +262,7 @@ public class TestContextManager {
|
||||
* {@link Method test method}, for example for tearing down test fixtures,
|
||||
* ending a transaction, etc. Should be called after any framework-specific
|
||||
* <em>after methods</em> (e.g., methods annotated with JUnit's
|
||||
* {@link org.junit.After @After}).
|
||||
* {@link org.junit.After @After}).
|
||||
* <p>The managed {@link TestContext} will be updated with the supplied
|
||||
* {@code testInstance}, {@code testMethod}, and
|
||||
* {@code exception}.
|
||||
@@ -421,7 +314,7 @@ public class TestContextManager {
|
||||
* Hook for post-processing a test class <em>after</em> execution of all
|
||||
* tests within the class. Should be called after any framework-specific
|
||||
* <em>after class methods</em> (e.g., methods annotated with JUnit's
|
||||
* {@link org.junit.AfterClass @AfterClass}).
|
||||
* {@link org.junit.AfterClass @AfterClass}).
|
||||
* <p>Each registered {@link TestExecutionListener} will be given a chance to
|
||||
* post-process the test class. If a listener throws an exception, the
|
||||
* remaining registered listeners will still be called, but the first
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -46,10 +46,21 @@ import java.lang.annotation.Target;
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface TestExecutionListeners {
|
||||
|
||||
/**
|
||||
* Alias for {@link #listeners() listeners}.
|
||||
*
|
||||
* <p>This attribute may <strong>not</strong> be used in conjunction with
|
||||
* {@link #listeners}, but it may be used instead of {@link #listeners}.
|
||||
*/
|
||||
Class<? extends TestExecutionListener>[] value() default {};
|
||||
|
||||
/**
|
||||
* The {@link TestExecutionListener TestExecutionListeners} to register with
|
||||
* a {@link TestContextManager}.
|
||||
*
|
||||
* <p>This attribute may <strong>not</strong> be used in conjunction with
|
||||
* {@link #value}, but it may be used instead of {@link #value}.
|
||||
*
|
||||
* @see org.springframework.test.context.web.ServletTestExecutionListener
|
||||
* @see org.springframework.test.context.support.DependencyInjectionTestExecutionListener
|
||||
* @see org.springframework.test.context.support.DirtiesContextTestExecutionListener
|
||||
@@ -57,11 +68,6 @@ public @interface TestExecutionListeners {
|
||||
*/
|
||||
Class<? extends TestExecutionListener>[] listeners() default {};
|
||||
|
||||
/**
|
||||
* Alias for {@link #listeners() listeners}.
|
||||
*/
|
||||
Class<? extends TestExecutionListener>[] value() default {};
|
||||
|
||||
/**
|
||||
* Whether or not {@link #value() TestExecutionListeners} from superclasses
|
||||
* should be <em>inherited</em>.
|
||||
|
||||
@@ -50,36 +50,32 @@ import org.springframework.test.context.junit4.statements.SpringRepeat;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* {@code SpringJUnit4ClassRunner} is a custom extension of
|
||||
* <p>{@code SpringJUnit4ClassRunner} is a custom extension of JUnit's
|
||||
* {@link BlockJUnit4ClassRunner} which provides functionality of the
|
||||
* <em>Spring TestContext Framework</em> to standard JUnit 4.5+ tests by means
|
||||
* of the {@link TestContextManager} and associated support classes and
|
||||
* annotations.
|
||||
* </p>
|
||||
* <p>
|
||||
* The following list constitutes all annotations currently supported directly
|
||||
*
|
||||
* <p>The following list constitutes all annotations currently supported directly
|
||||
* by {@code SpringJUnit4ClassRunner}.
|
||||
* <em>(Note that additional annotations may be supported by various
|
||||
* {@link org.springframework.test.context.TestExecutionListener
|
||||
* TestExecutionListeners})</em>
|
||||
* </p>
|
||||
* <em>(Note that additional annotations may be supported by various {@link
|
||||
* org.springframework.test.context.TestExecutionListener TestExecutionListeners}
|
||||
* or {@link org.springframework.test.context.TestContextBootstrapper
|
||||
* TestContextBootstrapper} implementations)</em>
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link Test#expected() @Test(expected=...)}</li>
|
||||
* <li>{@link Test#timeout() @Test(timeout=...)}</li>
|
||||
* <li>{@link Timed @Timed}</li>
|
||||
* <li>{@link Repeat @Repeat}</li>
|
||||
* <li>{@link Ignore @Ignore}</li>
|
||||
* <li>
|
||||
* {@link org.springframework.test.annotation.ProfileValueSourceConfiguration
|
||||
* <li>{@link org.springframework.test.annotation.ProfileValueSourceConfiguration
|
||||
* @ProfileValueSourceConfiguration}</li>
|
||||
* <li>{@link org.springframework.test.annotation.IfProfileValue
|
||||
* @IfProfileValue}</li>
|
||||
* <li>{@link org.springframework.test.annotation.IfProfileValue @IfProfileValue}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* <b>NOTE:</b> As of Spring 3.0, {@code SpringJUnit4ClassRunner} requires
|
||||
* JUnit 4.5+.
|
||||
* </p>
|
||||
*
|
||||
* <p><strong>NOTE:</strong> As of Spring 3.0, {@code SpringJUnit4ClassRunner}
|
||||
* requires JUnit 4.5 or higher.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
@@ -110,14 +106,12 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link TestContextManager} for the supplied test class and
|
||||
* the configured <em>default {@code ContextLoader} class name</em>.
|
||||
* Can be overridden by subclasses.
|
||||
* Creates a new {@link TestContextManager} for the supplied test class.
|
||||
* <p>Can be overridden by subclasses.
|
||||
* @param clazz the test class to be managed
|
||||
* @see #getDefaultContextLoaderClassName(Class)
|
||||
*/
|
||||
protected TestContextManager createTestContextManager(Class<?> clazz) {
|
||||
return new TestContextManager(clazz, getDefaultContextLoaderClassName(clazz));
|
||||
return new TestContextManager(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,21 +121,6 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner {
|
||||
return this.testContextManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the default {@code ContextLoader} class to use for
|
||||
* the supplied test class. The named class will be used if the test class
|
||||
* does not explicitly declare a {@code ContextLoader} class via the
|
||||
* {@code @ContextConfiguration} annotation.
|
||||
* <p>The default implementation returns {@code null}, thus implying use
|
||||
* of the <em>standard</em> default {@code ContextLoader} class name.
|
||||
* Can be overridden by subclasses.
|
||||
* @param clazz the test class
|
||||
* @return {@code null}
|
||||
*/
|
||||
protected String getDefaultContextLoaderClassName(Class<?> clazz) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a description suitable for an ignored test class if the test is
|
||||
* disabled via {@code @IfProfileValue} at the class-level, and
|
||||
|
||||
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
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.beans.BeanUtils;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.test.context.BootstrapContext;
|
||||
import org.springframework.test.context.CacheAwareContextLoaderDelegate;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.SmartContextLoader;
|
||||
import org.springframework.test.context.TestContextBootstrapper;
|
||||
import org.springframework.test.context.TestExecutionListener;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.util.MetaAnnotationUtils;
|
||||
import org.springframework.test.util.MetaAnnotationUtils.AnnotationDescriptor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Abstract implementation of the {@link TestContextBootstrapper} interface which
|
||||
* provides most of the behavior required by a bootstrapper.
|
||||
*
|
||||
* <p>Concrete subclasses typically will only need to provide implementations for
|
||||
* the following {@code abstract} methods:
|
||||
* <ul>
|
||||
* <li>{@link #getDefaultTestExecutionListenerClassNames()}
|
||||
* <li>{@link #getDefaultContextLoaderClass(Class)}
|
||||
* <li>{@link #buildMergedContextConfiguration(Class, String[], Class[], Set, String[], ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)}
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
*/
|
||||
public abstract class AbstractTestContextBootstrapper implements TestContextBootstrapper {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AbstractTestContextBootstrapper.class);
|
||||
|
||||
private BootstrapContext bootstrapContext;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setBootstrapContext(BootstrapContext bootstrapContext) {
|
||||
this.bootstrapContext = bootstrapContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public BootstrapContext getBootstrapContext() {
|
||||
return this.bootstrapContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public final List<TestExecutionListener> getTestExecutionListeners() {
|
||||
Class<?> clazz = getBootstrapContext().getTestClass();
|
||||
Class<TestExecutionListeners> annotationType = TestExecutionListeners.class;
|
||||
List<Class<? extends TestExecutionListener>> classesList = new ArrayList<Class<? extends TestExecutionListener>>();
|
||||
|
||||
AnnotationDescriptor<TestExecutionListeners> descriptor = MetaAnnotationUtils.findAnnotationDescriptor(clazz,
|
||||
annotationType);
|
||||
|
||||
// Use defaults?
|
||||
if (descriptor == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("@TestExecutionListeners is not present for class [" + clazz + "]: using defaults.");
|
||||
}
|
||||
classesList.addAll(getDefaultTestExecutionListenerClasses());
|
||||
}
|
||||
else {
|
||||
// Traverse the class hierarchy...
|
||||
while (descriptor != null) {
|
||||
Class<?> declaringClass = descriptor.getDeclaringClass();
|
||||
|
||||
AnnotationAttributes annAttrs = descriptor.getAnnotationAttributes();
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format(
|
||||
"Retrieved @TestExecutionListeners attributes [%s] for declaring class [%s].", annAttrs,
|
||||
declaringClass));
|
||||
}
|
||||
|
||||
Class<? extends TestExecutionListener>[] valueListenerClasses = (Class<? extends TestExecutionListener>[]) annAttrs.getClassArray("value");
|
||||
Class<? extends TestExecutionListener>[] listenerClasses = (Class<? extends TestExecutionListener>[]) annAttrs.getClassArray("listeners");
|
||||
if (!ObjectUtils.isEmpty(valueListenerClasses) && !ObjectUtils.isEmpty(listenerClasses)) {
|
||||
String msg = String.format(
|
||||
"Class [%s] has been configured with @TestExecutionListeners' 'value' [%s] "
|
||||
+ "and 'listeners' [%s] attributes. Use one or the other, but not both.",
|
||||
declaringClass, ObjectUtils.nullSafeToString(valueListenerClasses),
|
||||
ObjectUtils.nullSafeToString(listenerClasses));
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
else if (!ObjectUtils.isEmpty(valueListenerClasses)) {
|
||||
listenerClasses = valueListenerClasses;
|
||||
}
|
||||
|
||||
if (listenerClasses != null) {
|
||||
classesList.addAll(0, Arrays.<Class<? extends TestExecutionListener>> asList(listenerClasses));
|
||||
}
|
||||
|
||||
descriptor = (annAttrs.getBoolean("inheritListeners") ? MetaAnnotationUtils.findAnnotationDescriptor(
|
||||
descriptor.getRootDeclaringClass().getSuperclass(), annotationType) : null);
|
||||
}
|
||||
}
|
||||
|
||||
List<TestExecutionListener> listeners = new ArrayList<TestExecutionListener>(classesList.size());
|
||||
for (Class<? extends TestExecutionListener> listenerClass : classesList) {
|
||||
try {
|
||||
listeners.add(BeanUtils.instantiateClass(listenerClass));
|
||||
}
|
||||
catch (NoClassDefFoundError err) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("Could not instantiate TestExecutionListener [%s]. "
|
||||
+ "Specify custom listener classes or make the default listener classes "
|
||||
+ "(and their dependencies) available.", listenerClass.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return listeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default {@link TestExecutionListener} classes for this bootstrapper.
|
||||
* <p>This method is invoked by {@link #getTestExecutionListeners()} and
|
||||
* delegates to {@link #getDefaultTestExecutionListenerClassNames()} to
|
||||
* retrieve the class names.
|
||||
* <p>If a particular class cannot be loaded, a {@code DEBUG} message will
|
||||
* be logged, but the associated exception will not be rethrown.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Set<Class<? extends TestExecutionListener>> getDefaultTestExecutionListenerClasses() {
|
||||
Set<Class<? extends TestExecutionListener>> defaultListenerClasses = new LinkedHashSet<Class<? extends TestExecutionListener>>();
|
||||
for (String className : getDefaultTestExecutionListenerClassNames()) {
|
||||
try {
|
||||
defaultListenerClasses.add((Class<? extends TestExecutionListener>) getClass().getClassLoader().loadClass(
|
||||
className));
|
||||
}
|
||||
catch (Throwable t) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not load default TestExecutionListener class [" + className
|
||||
+ "]. Specify custom listener classes or make the default listener classes available.", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultListenerClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
@Override
|
||||
public final MergedContextConfiguration buildMergedContextConfiguration() {
|
||||
Class<?> testClass = getBootstrapContext().getTestClass();
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = getBootstrapContext().getCacheAwareContextLoaderDelegate();
|
||||
|
||||
if (MetaAnnotationUtils.findAnnotationDescriptorForTypes(testClass, ContextConfiguration.class,
|
||||
ContextHierarchy.class) == null) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format(
|
||||
"Neither @ContextConfiguration nor @ContextHierarchy found for test class [%s]",
|
||||
testClass.getName()));
|
||||
}
|
||||
return new MergedContextConfiguration(testClass, null, null, null, null);
|
||||
}
|
||||
|
||||
if (AnnotationUtils.findAnnotation(testClass, ContextHierarchy.class) != null) {
|
||||
Map<String, List<ContextConfigurationAttributes>> hierarchyMap = ContextLoaderUtils.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, parentConfig,
|
||||
cacheAwareContextLoaderDelegate);
|
||||
parentConfig = mergedConfig;
|
||||
}
|
||||
|
||||
// Return the last level in the context hierarchy
|
||||
return mergedConfig;
|
||||
}
|
||||
else {
|
||||
return buildMergedContextConfiguration(testClass,
|
||||
ContextLoaderUtils.resolveContextConfigurationAttributes(testClass), null,
|
||||
cacheAwareContextLoaderDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the {@link MergedContextConfiguration merged context configuration}
|
||||
* for the supplied {@link Class testClass}, context configuration attributes,
|
||||
* 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 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 ContextLoaderUtils#resolveContextConfigurationAttributes
|
||||
* @see SmartContextLoader#processContextConfiguration
|
||||
* @see ContextLoader#processLocations
|
||||
* @see ActiveProfilesUtils#resolveActiveProfiles
|
||||
* @see ApplicationContextInitializerUtils#resolveInitializerClasses
|
||||
* @see MergedContextConfiguration
|
||||
*/
|
||||
private MergedContextConfiguration buildMergedContextConfiguration(final Class<?> testClass,
|
||||
final List<ContextConfigurationAttributes> configAttributesList, MergedContextConfiguration parentConfig,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
|
||||
|
||||
final ContextLoader contextLoader = resolveContextLoader(testClass, configAttributesList);
|
||||
final List<String> locationsList = new ArrayList<String>();
|
||||
final List<Class<?>> classesList = new ArrayList<Class<?>>();
|
||||
|
||||
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Processing locations and classes for context configuration attributes %s",
|
||||
configAttributes));
|
||||
}
|
||||
|
||||
if (contextLoader instanceof SmartContextLoader) {
|
||||
SmartContextLoader smartContextLoader = (SmartContextLoader) contextLoader;
|
||||
smartContextLoader.processContextConfiguration(configAttributes);
|
||||
locationsList.addAll(0, Arrays.asList(configAttributes.getLocations()));
|
||||
classesList.addAll(0, Arrays.asList(configAttributes.getClasses()));
|
||||
}
|
||||
else {
|
||||
String[] processedLocations = contextLoader.processLocations(configAttributes.getDeclaringClass(),
|
||||
configAttributes.getLocations());
|
||||
locationsList.addAll(0, Arrays.asList(processedLocations));
|
||||
// Legacy ContextLoaders don't know how to process classes
|
||||
}
|
||||
|
||||
if (!configAttributes.isInheritLocations()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String[] locations = StringUtils.toStringArray(locationsList);
|
||||
Class<?>[] classes = ClassUtils.toClassArray(classesList);
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses = //
|
||||
ApplicationContextInitializerUtils.resolveInitializerClasses(configAttributesList);
|
||||
String[] activeProfiles = ActiveProfilesUtils.resolveActiveProfiles(testClass);
|
||||
|
||||
return buildMergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
|
||||
contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 user has not explicitly declared which loader to use, the value
|
||||
* returned from {@link #getDefaultContextLoaderClass} will be used as the
|
||||
* default context loader class. For details on the class resolution process,
|
||||
* see {@link #resolveExplicitContextLoaderClass} and
|
||||
* {@link #getDefaultContextLoaderClass}.
|
||||
*
|
||||
* @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)
|
||||
* @return the resolved {@code ContextLoader} for the supplied {@code testClass}
|
||||
* (never {@code null})
|
||||
*/
|
||||
private ContextLoader resolveContextLoader(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributesList) {
|
||||
Assert.notNull(testClass, "Class must not be null");
|
||||
Assert.notEmpty(configAttributesList, "ContextConfigurationAttributes list must not be empty");
|
||||
|
||||
Class<? extends ContextLoader> contextLoaderClass = resolveExplicitContextLoaderClass(configAttributesList);
|
||||
if (contextLoaderClass == null) {
|
||||
contextLoaderClass = getDefaultContextLoaderClass(testClass);
|
||||
}
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Using ContextLoader class [%s] for test class [%s]",
|
||||
contextLoaderClass.getName(), testClass.getName()));
|
||||
}
|
||||
|
||||
return BeanUtils.instantiateClass(contextLoaderClass, ContextLoader.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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:
|
||||
*
|
||||
* <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>
|
||||
* </ol>
|
||||
*
|
||||
* @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 {@code ContextLoader} class to use for the supplied configuration
|
||||
* attributes, or {@code null} if no explicit loader is found
|
||||
* @throws IllegalArgumentException if supplied configuration attributes are
|
||||
* {@code null} or <em>empty</em>
|
||||
*/
|
||||
private Class<? extends ContextLoader> resolveExplicitContextLoaderClass(
|
||||
List<ContextConfigurationAttributes> configAttributesList) {
|
||||
Assert.notEmpty(configAttributesList, "ContextConfigurationAttributes list must not be empty");
|
||||
|
||||
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Resolving ContextLoader for context configuration attributes %s",
|
||||
configAttributes));
|
||||
}
|
||||
|
||||
Class<? extends ContextLoader> contextLoaderClass = configAttributes.getContextLoaderClass();
|
||||
if (!ContextLoader.class.equals(contextLoaderClass)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Found explicit ContextLoader class [%s] for context configuration attributes %s",
|
||||
contextLoaderClass.getName(), configAttributes));
|
||||
}
|
||||
return contextLoaderClass;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the names of the default {@link TestExecutionListener} classes for
|
||||
* this bootstrapper.
|
||||
* <p>This method is invoked by {@link #getDefaultTestExecutionListenerClasses()}.
|
||||
* @return an <em>unmodifiable</em> list of names of default {@code
|
||||
* TestExecutionListener} classes
|
||||
*/
|
||||
protected abstract List<String> getDefaultTestExecutionListenerClassNames();
|
||||
|
||||
/**
|
||||
* Determine the default {@link ContextLoader} class to use for the supplied
|
||||
* test class.
|
||||
* <p>The class returned by this method will only be used if a {@code ContextLoader}
|
||||
* class has not been explicitly declared via {@link ContextConfiguration#loader}.
|
||||
* @param testClass the test class for which to retrieve the default
|
||||
* {@code ContextLoader} class
|
||||
*/
|
||||
protected abstract Class<? extends ContextLoader> getDefaultContextLoaderClass(Class<?> testClass);
|
||||
|
||||
/**
|
||||
* Build a {@link MergedContextConfiguration} instance from the supplied,
|
||||
* merged values.
|
||||
*
|
||||
* <p>Concrete subclasses typically will only need to instantiate
|
||||
* {@link MergedContextConfiguration} (or a specialized subclass thereof)
|
||||
* from the provided values; further processing and merging of values is likely
|
||||
* unnecessary.
|
||||
*
|
||||
* @param testClass the test class for which the {@code MergedContextConfiguration}
|
||||
* should be built (must not be {@code null})
|
||||
* @param locations the merged resource locations
|
||||
* @param classes the merged annotated classes
|
||||
* @param initializerClasses the merged context initializer classes
|
||||
* @param activeProfiles the merged active bean definition profiles
|
||||
* @param contextLoader the resolved {@code ContextLoader}
|
||||
* @param cacheAwareContextLoaderDelegate the cache-aware context loader delegate to
|
||||
* be provided to the instantiated {@code MergedContextConfiguration}
|
||||
* @param parentConfig the merged context configuration for the parent application
|
||||
* context in a context hierarchy, or {@code null} if there is no parent
|
||||
* @return the fully initialized {@code MergedContextConfiguration}
|
||||
*/
|
||||
protected abstract MergedContextConfiguration buildMergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
String[] locations,
|
||||
Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses,
|
||||
String[] activeProfiles, ContextLoader contextLoader,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parentConfig);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.support;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.ActiveProfilesResolver;
|
||||
import org.springframework.test.util.MetaAnnotationUtils;
|
||||
import org.springframework.test.util.MetaAnnotationUtils.AnnotationDescriptor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Utility methods for working with {@link ActiveProfiles @ActiveProfiles} and
|
||||
* {@link ActiveProfilesResolver ActiveProfilesResolvers}.
|
||||
*
|
||||
* <p>Although {@code ActiveProfilesUtils} was first introduced in Spring Framework
|
||||
* 4.1, the initial implementations of methods in this class were based on the
|
||||
* existing code base in {@code ContextLoaderUtils}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Michail Nikolaev
|
||||
* @since 4.1
|
||||
* @see ActiveProfiles
|
||||
* @see ActiveProfilesResolver
|
||||
*/
|
||||
abstract class ActiveProfilesUtils {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ActiveProfilesUtils.class);
|
||||
|
||||
|
||||
private ActiveProfilesUtils() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve <em>active bean definition profiles</em> for the supplied {@link Class}.
|
||||
*
|
||||
* <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})
|
||||
* @see ActiveProfiles
|
||||
* @see ActiveProfilesResolver
|
||||
* @see org.springframework.context.annotation.Profile
|
||||
*/
|
||||
static String[] resolveActiveProfiles(Class<?> testClass) {
|
||||
Assert.notNull(testClass, "Class must not be null");
|
||||
|
||||
final Set<String> activeProfiles = new HashSet<String>();
|
||||
|
||||
Class<ActiveProfiles> annotationType = ActiveProfiles.class;
|
||||
AnnotationDescriptor<ActiveProfiles> descriptor = MetaAnnotationUtils.findAnnotationDescriptor(testClass,
|
||||
annotationType);
|
||||
if (descriptor == null && logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]",
|
||||
annotationType.getName(), testClass.getName()));
|
||||
}
|
||||
|
||||
while (descriptor != null) {
|
||||
Class<?> rootDeclaringClass = descriptor.getRootDeclaringClass();
|
||||
Class<?> declaringClass = descriptor.getDeclaringClass();
|
||||
|
||||
AnnotationAttributes annAttrs = descriptor.getAnnotationAttributes();
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Retrieved @ActiveProfiles attributes [%s] for declaring class [%s].",
|
||||
annAttrs, declaringClass.getName()));
|
||||
}
|
||||
validateActiveProfilesConfiguration(declaringClass, annAttrs);
|
||||
|
||||
Class<? extends ActiveProfilesResolver> resolverClass = annAttrs.getClass("resolver");
|
||||
if (ActiveProfilesResolver.class.equals(resolverClass)) {
|
||||
resolverClass = DefaultActiveProfilesResolver.class;
|
||||
}
|
||||
|
||||
ActiveProfilesResolver resolver = null;
|
||||
try {
|
||||
resolver = BeanUtils.instantiateClass(resolverClass, ActiveProfilesResolver.class);
|
||||
}
|
||||
catch (Exception e) {
|
||||
String msg = String.format("Could not instantiate ActiveProfilesResolver of "
|
||||
+ "type [%s] for test class [%s].", resolverClass.getName(), rootDeclaringClass.getName());
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg, e);
|
||||
}
|
||||
|
||||
String[] profiles = resolver.resolve(rootDeclaringClass);
|
||||
if (profiles == null) {
|
||||
String msg = String.format(
|
||||
"ActiveProfilesResolver [%s] returned a null array of bean definition profiles.",
|
||||
resolverClass.getName());
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
for (String profile : profiles) {
|
||||
if (StringUtils.hasText(profile)) {
|
||||
activeProfiles.add(profile.trim());
|
||||
}
|
||||
}
|
||||
|
||||
descriptor = annAttrs.getBoolean("inheritProfiles") ? MetaAnnotationUtils.findAnnotationDescriptor(
|
||||
rootDeclaringClass.getSuperclass(), annotationType) : null;
|
||||
}
|
||||
|
||||
return StringUtils.toStringArray(activeProfiles);
|
||||
}
|
||||
|
||||
private static void validateActiveProfilesConfiguration(Class<?> declaringClass, AnnotationAttributes annAttrs) {
|
||||
String[] valueProfiles = annAttrs.getStringArray("value");
|
||||
String[] profiles = annAttrs.getStringArray("profiles");
|
||||
boolean valueDeclared = !ObjectUtils.isEmpty(valueProfiles);
|
||||
boolean profilesDeclared = !ObjectUtils.isEmpty(profiles);
|
||||
|
||||
if (valueDeclared && profilesDeclared) {
|
||||
String msg = String.format("Class [%s] has been configured with @ActiveProfiles' 'value' [%s] "
|
||||
+ "and 'profiles' [%s] attributes. Only one declaration of active bean "
|
||||
+ "definition profiles is permitted per @ActiveProfiles annotation.", declaringClass.getName(),
|
||||
ObjectUtils.nullSafeToString(valueProfiles), ObjectUtils.nullSafeToString(profiles));
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.support;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility methods for working with {@link ApplicationContextInitializer
|
||||
* ApplicationContextInitializers}.
|
||||
*
|
||||
* <p>Although {@code ApplicationContextInitializerUtils} was first introduced
|
||||
* in Spring Framework 4.1, the initial implementations of methods in this class
|
||||
* were based on the existing code base in {@code ContextLoaderUtils}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
* @see ContextConfiguration#initializers
|
||||
*/
|
||||
abstract class ApplicationContextInitializerUtils {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ApplicationContextInitializerUtils.class);
|
||||
|
||||
|
||||
private ApplicationContextInitializerUtils() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the set 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.
|
||||
*
|
||||
* @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})
|
||||
* @since 3.2
|
||||
*/
|
||||
static Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> resolveInitializerClasses(
|
||||
List<ContextConfigurationAttributes> configAttributesList) {
|
||||
Assert.notEmpty(configAttributesList, "ContextConfigurationAttributes list must not be empty");
|
||||
|
||||
final Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses = //
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
|
||||
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Processing context initializers for context configuration attributes %s",
|
||||
configAttributes));
|
||||
}
|
||||
|
||||
initializerClasses.addAll(Arrays.asList(configAttributes.getInitializers()));
|
||||
|
||||
if (!configAttributes.isInheritInitializers()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return initializerClasses;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.SmartContextLoader;
|
||||
import org.springframework.test.util.MetaAnnotationUtils.AnnotationDescriptor;
|
||||
import org.springframework.test.util.MetaAnnotationUtils.UntypedAnnotationDescriptor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
import static org.springframework.test.util.MetaAnnotationUtils.*;
|
||||
|
||||
/**
|
||||
* Utility methods for working with {@link ContextLoader ContextLoaders} and
|
||||
* {@link SmartContextLoader SmartContextLoaders} and resolving resource locations,
|
||||
* annotated classes, and application context initializers.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.1
|
||||
* @see ContextLoader
|
||||
* @see SmartContextLoader
|
||||
* @see ContextConfiguration
|
||||
* @see ContextConfigurationAttributes
|
||||
* @see ApplicationContextInitializer
|
||||
* @see ContextHierarchy
|
||||
*/
|
||||
abstract class ContextLoaderUtils {
|
||||
|
||||
static final String GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX = "ContextHierarchyLevel#";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ContextLoaderUtils.class);
|
||||
|
||||
|
||||
private ContextLoaderUtils() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for creating a {@link ContextConfigurationAttributes}
|
||||
* instance from the supplied {@link ContextConfiguration} annotation 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for creating a {@link ContextConfigurationAttributes}
|
||||
* instance from the supplied {@link AnnotationAttributes} and declaring
|
||||
* class and then adding the attributes to the supplied list.
|
||||
* @since 4.0
|
||||
*/
|
||||
private static void convertAnnotationAttributesToConfigAttributesAndAddToList(AnnotationAttributes annAttrs,
|
||||
Class<?> declaringClass, final List<ContextConfigurationAttributes> attributesList) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Retrieved @ContextConfiguration attributes [%s] for declaring class [%s].",
|
||||
annAttrs, declaringClass.getName()));
|
||||
}
|
||||
|
||||
ContextConfigurationAttributes attributes = new ContextConfigurationAttributes(declaringClass, annAttrs);
|
||||
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>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; or if a test class or composed annotation
|
||||
* in the class hierarchy declares both {@code @ContextConfiguration} and
|
||||
* {@code @ContextHierarchy} as top-level annotations.
|
||||
* @throws IllegalStateException if no class in the class hierarchy declares
|
||||
* {@code @ContextHierarchy}.
|
||||
*
|
||||
* @since 3.2.2
|
||||
* @see #buildContextHierarchyMap(Class)
|
||||
* @see #resolveContextConfigurationAttributes(Class)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static List<List<ContextConfigurationAttributes>> resolveContextHierarchyAttributes(Class<?> testClass) {
|
||||
Assert.notNull(testClass, "Class must not be null");
|
||||
Assert.state(findAnnotation(testClass, ContextHierarchy.class) != null, "@ContextHierarchy must be present");
|
||||
|
||||
final Class<ContextConfiguration> contextConfigType = ContextConfiguration.class;
|
||||
final Class<ContextHierarchy> contextHierarchyType = ContextHierarchy.class;
|
||||
final List<List<ContextConfigurationAttributes>> hierarchyAttributes = new ArrayList<List<ContextConfigurationAttributes>>();
|
||||
|
||||
UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(testClass, contextConfigType,
|
||||
contextHierarchyType);
|
||||
Assert.notNull(descriptor, 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 (descriptor != null) {
|
||||
Class<?> rootDeclaringClass = descriptor.getRootDeclaringClass();
|
||||
Class<?> declaringClass = descriptor.getDeclaringClass();
|
||||
|
||||
boolean contextConfigDeclaredLocally = isAnnotationDeclaredLocally(contextConfigType, declaringClass);
|
||||
boolean contextHierarchyDeclaredLocally = isAnnotationDeclaredLocally(contextHierarchyType, declaringClass);
|
||||
|
||||
if (contextConfigDeclaredLocally && contextHierarchyDeclaredLocally) {
|
||||
String msg = String.format("Class [%s] has been configured with both @ContextConfiguration "
|
||||
+ "and @ContextHierarchy. Only one of these annotations may be declared on a test class "
|
||||
+ "or composed annotation.", declaringClass.getName());
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
final List<ContextConfigurationAttributes> configAttributesList = new ArrayList<ContextConfigurationAttributes>();
|
||||
|
||||
if (contextConfigDeclaredLocally) {
|
||||
convertAnnotationAttributesToConfigAttributesAndAddToList(descriptor.getAnnotationAttributes(),
|
||||
rootDeclaringClass, configAttributesList);
|
||||
}
|
||||
else if (contextHierarchyDeclaredLocally) {
|
||||
ContextHierarchy contextHierarchy = getAnnotation(declaringClass, contextHierarchyType);
|
||||
for (ContextConfiguration contextConfiguration : contextHierarchy.value()) {
|
||||
convertContextConfigToConfigAttributesAndAddToList(contextConfiguration, rootDeclaringClass,
|
||||
configAttributesList);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This should theoretically never happen...
|
||||
String msg = String.format("Test class [%s] has been configured with neither @ContextConfiguration "
|
||||
+ "nor @ContextHierarchy as a class-level annotation.", rootDeclaringClass.getName());
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
hierarchyAttributes.add(0, configAttributesList);
|
||||
|
||||
descriptor = findAnnotationDescriptorForTypes(rootDeclaringClass.getSuperclass(), contextConfigType,
|
||||
contextHierarchyType);
|
||||
}
|
||||
|
||||
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}
|
||||
* @throws IllegalArgumentException if the lists of context configuration
|
||||
* attributes for each level in the {@code @ContextHierarchy} do not define
|
||||
* unique context configuration within the overall hierarchy.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for uniqueness
|
||||
Set<List<ContextConfigurationAttributes>> set = new HashSet<List<ContextConfigurationAttributes>>(map.values());
|
||||
if (set.size() != map.size()) {
|
||||
String msg = String.format("The @ContextConfiguration elements configured via "
|
||||
+ "@ContextHierarchy in test class [%s] and its superclasses must "
|
||||
+ "define unique contexts per hierarchy level.", testClass.getName());
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
static List<ContextConfigurationAttributes> resolveContextConfigurationAttributes(Class<?> testClass) {
|
||||
Assert.notNull(testClass, "Class must not be null");
|
||||
|
||||
final List<ContextConfigurationAttributes> attributesList = new ArrayList<ContextConfigurationAttributes>();
|
||||
|
||||
Class<ContextConfiguration> annotationType = ContextConfiguration.class;
|
||||
|
||||
AnnotationDescriptor<ContextConfiguration> descriptor = findAnnotationDescriptor(testClass, annotationType);
|
||||
Assert.notNull(descriptor, String.format(
|
||||
"Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]",
|
||||
annotationType.getName(), testClass.getName()));
|
||||
|
||||
while (descriptor != null) {
|
||||
convertAnnotationAttributesToConfigAttributesAndAddToList(descriptor.getAnnotationAttributes(),
|
||||
descriptor.getRootDeclaringClass(), attributesList);
|
||||
descriptor = findAnnotationDescriptor(descriptor.getRootDeclaringClass().getSuperclass(), annotationType);
|
||||
}
|
||||
|
||||
return attributesList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.support;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.ActiveProfilesResolver;
|
||||
import org.springframework.test.util.MetaAnnotationUtils.AnnotationDescriptor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.test.util.MetaAnnotationUtils.*;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link ActiveProfilesResolver} strategy that
|
||||
* resolves <em>active bean definition profiles</em> based solely on profiles
|
||||
* configured declaratively via {@link ActiveProfiles#profiles} or
|
||||
* {@link ActiveProfiles#value}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
* @see ActiveProfiles
|
||||
* @see ActiveProfilesResolver
|
||||
*/
|
||||
public class DefaultActiveProfilesResolver implements ActiveProfilesResolver {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DefaultActiveProfilesResolver.class);
|
||||
|
||||
|
||||
/**
|
||||
* Resolve the <em>bean definition profiles</em> for the given {@linkplain
|
||||
* Class test class} based on profiles configured declaratively via
|
||||
* {@link ActiveProfiles#profiles} or {@link ActiveProfiles#value}.
|
||||
* @param testClass the test class for which the profiles should be resolved;
|
||||
* never {@code null}
|
||||
* @return the list of bean definition profiles to use when loading the
|
||||
* {@code ApplicationContext}; never {@code null}
|
||||
*/
|
||||
@Override
|
||||
public String[] resolve(Class<?> testClass) {
|
||||
Assert.notNull(testClass, "Class must not be null");
|
||||
|
||||
final Set<String> activeProfiles = new HashSet<String>();
|
||||
|
||||
Class<ActiveProfiles> annotationType = ActiveProfiles.class;
|
||||
AnnotationDescriptor<ActiveProfiles> descriptor = findAnnotationDescriptor(testClass, annotationType);
|
||||
|
||||
if (descriptor == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]",
|
||||
annotationType.getName(), testClass.getName()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
Class<?> rootDeclaringClass = descriptor.getRootDeclaringClass();
|
||||
Class<?> declaringClass = descriptor.getDeclaringClass();
|
||||
|
||||
AnnotationAttributes annAttrs = descriptor.getAnnotationAttributes();
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Retrieved @ActiveProfiles attributes [%s] for declaring class [%s].",
|
||||
annAttrs, declaringClass.getName()));
|
||||
}
|
||||
|
||||
Class<? extends ActiveProfilesResolver> resolverClass = annAttrs.getClass("resolver");
|
||||
if (!ActiveProfilesResolver.class.equals(resolverClass)) {
|
||||
String msg = String.format("Configuration error for test class [%s]: %s cannot be used "
|
||||
+ "in conjunction with custom resolver [%s].", rootDeclaringClass.getName(),
|
||||
getClass().getSimpleName(), resolverClass.getName());
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
String[] profiles = annAttrs.getStringArray("profiles");
|
||||
String[] valueProfiles = annAttrs.getStringArray("value");
|
||||
boolean valueDeclared = !ObjectUtils.isEmpty(valueProfiles);
|
||||
boolean profilesDeclared = !ObjectUtils.isEmpty(profiles);
|
||||
|
||||
if (valueDeclared && profilesDeclared) {
|
||||
String msg = String.format("Class [%s] has been configured with @ActiveProfiles' 'value' [%s] "
|
||||
+ "and 'profiles' [%s] attributes. Only one declaration of active bean "
|
||||
+ "definition profiles is permitted per @ActiveProfiles annotation.", declaringClass.getName(),
|
||||
ObjectUtils.nullSafeToString(valueProfiles), ObjectUtils.nullSafeToString(profiles));
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
if (valueDeclared) {
|
||||
profiles = valueProfiles;
|
||||
}
|
||||
|
||||
for (String profile : profiles) {
|
||||
if (StringUtils.hasText(profile)) {
|
||||
activeProfiles.add(profile.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return StringUtils.toStringArray(activeProfiles);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.support;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.test.context.CacheAwareContextLoaderDelegate;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.TestContextBootstrapper;
|
||||
import org.springframework.test.context.TestExecutionListener;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link TestContextBootstrapper} SPI.
|
||||
*
|
||||
* <ul>
|
||||
* <li>Uses the following default {@link TestExecutionListener TestExecutionListeners}:
|
||||
* <ol>
|
||||
* <li>{@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener}
|
||||
* <li>{@link org.springframework.test.context.support.DirtiesContextTestExecutionListener}
|
||||
* <li>{@link org.springframework.test.context.transaction.TransactionalTestExecutionListener}
|
||||
* </ol>
|
||||
* <li>Uses {@link DelegatingSmartContextLoader} as the default {@link ContextLoader}.
|
||||
* <li>Builds a standard {@link MergedContextConfiguration}.
|
||||
* </ul>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
*/
|
||||
public class DefaultTestContextBootstrapper extends AbstractTestContextBootstrapper {
|
||||
|
||||
private static final List<String> DEFAULT_TEST_EXECUTION_LISTENER_CLASS_NAMES = Collections.unmodifiableList(Arrays.asList(
|
||||
"org.springframework.test.context.support.DependencyInjectionTestExecutionListener",
|
||||
"org.springframework.test.context.support.DirtiesContextTestExecutionListener",
|
||||
"org.springframework.test.context.transaction.TransactionalTestExecutionListener"));
|
||||
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable list of fully qualified class names for the following
|
||||
* default {@link TestExecutionListener TestExecutionListeners}:
|
||||
* <ol>
|
||||
* <li>{@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener}
|
||||
* <li>{@link org.springframework.test.context.support.DirtiesContextTestExecutionListener}
|
||||
* <li>{@link org.springframework.test.context.transaction.TransactionalTestExecutionListener}
|
||||
* </ol>
|
||||
*/
|
||||
protected List<String> getDefaultTestExecutionListenerClassNames() {
|
||||
return DEFAULT_TEST_EXECUTION_LISTENER_CLASS_NAMES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link DelegatingSmartContextLoader}.
|
||||
*/
|
||||
@Override
|
||||
protected Class<? extends ContextLoader> getDefaultContextLoaderClass(Class<?> testClass) {
|
||||
return DelegatingSmartContextLoader.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a standard {@link MergedContextConfiguration}.
|
||||
*/
|
||||
protected MergedContextConfiguration buildMergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
String[] locations,
|
||||
Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses,
|
||||
String[] activeProfiles, ContextLoader contextLoader,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parentConfig) {
|
||||
|
||||
return new MergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
|
||||
contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,8 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.test.context.BootstrapWith;
|
||||
|
||||
/**
|
||||
* {@code @WebAppConfiguration} is a class-level annotation that is used to
|
||||
* declare that the {@code ApplicationContext} loaded for an integration test
|
||||
@@ -51,6 +53,7 @@ import java.lang.annotation.Target;
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@BootstrapWith(WebTestContextBootstrapper.class)
|
||||
public @interface WebAppConfiguration {
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.web;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.test.context.CacheAwareContextLoaderDelegate;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.TestContextBootstrapper;
|
||||
import org.springframework.test.context.TestExecutionListener;
|
||||
import org.springframework.test.context.support.DefaultTestContextBootstrapper;
|
||||
|
||||
/**
|
||||
* Web-specific implementation of the {@link TestContextBootstrapper} SPI.
|
||||
*
|
||||
* <ul>
|
||||
* <li>Prepends {@link org.springframework.test.context.web.ServletTestExecutionListener}
|
||||
* to the list of default {@link TestExecutionListener TestExecutionListeners} supported by
|
||||
* the superclass.
|
||||
* <li>Uses {@link WebDelegatingSmartContextLoader} as the default {@link ContextLoader}
|
||||
* if the test class is annotated with {@link WebAppConfiguration @WebAppConfiguration}
|
||||
* and otherwise delegates to the superclass.
|
||||
* <li>Builds a {@link WebMergedContextConfiguration} if the test class is annotated
|
||||
* with {@link WebAppConfiguration @WebAppConfiguration} and otherwise delegates to
|
||||
* the superclass.
|
||||
* </ul>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
*/
|
||||
public class WebTestContextBootstrapper extends DefaultTestContextBootstrapper {
|
||||
|
||||
/**
|
||||
* Prepends {@link org.springframework.test.context.web.ServletTestExecutionListener}
|
||||
* to the list of default {@link TestExecutionListener TestExecutionListeners}
|
||||
* supported by the superclass and returns an unmodifiable, updated list.
|
||||
*/
|
||||
@Override
|
||||
protected List<String> getDefaultTestExecutionListenerClassNames() {
|
||||
List<String> classNames = new ArrayList<String>(super.getDefaultTestExecutionListenerClassNames());
|
||||
classNames.add(0, "org.springframework.test.context.web.ServletTestExecutionListener");
|
||||
return Collections.unmodifiableList(classNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link WebDelegatingSmartContextLoader} if the supplied class is
|
||||
* annotated with {@link WebAppConfiguration @WebAppConfiguration} and
|
||||
* otherwise delegates to the superclass.
|
||||
*/
|
||||
@Override
|
||||
protected Class<? extends ContextLoader> getDefaultContextLoaderClass(Class<?> testClass) {
|
||||
if (AnnotationUtils.findAnnotation(testClass, WebAppConfiguration.class) != null) {
|
||||
return WebDelegatingSmartContextLoader.class;
|
||||
}
|
||||
|
||||
// else...
|
||||
return super.getDefaultContextLoaderClass(testClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link WebMergedContextConfiguration} if the supplied class is
|
||||
* annotated with {@link WebAppConfiguration @WebAppConfiguration} and
|
||||
* otherwise delegates to the superclass.
|
||||
*/
|
||||
@Override
|
||||
protected MergedContextConfiguration buildMergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
String[] locations,
|
||||
Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses,
|
||||
String[] activeProfiles, ContextLoader contextLoader,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parentConfig) {
|
||||
|
||||
WebAppConfiguration webAppConfiguration = AnnotationUtils.findAnnotation(testClass, WebAppConfiguration.class);
|
||||
if (webAppConfiguration != null) {
|
||||
String resourceBasePath = webAppConfiguration.value();
|
||||
|
||||
return new WebMergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
|
||||
resourceBasePath, contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
|
||||
}
|
||||
|
||||
// else...
|
||||
return super.buildMergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
|
||||
contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context;
|
||||
package org.springframework.test.util;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.HashSet;
|
||||
@@ -27,8 +27,6 @@ import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
|
||||
/**
|
||||
* {@code MetaAnnotationUtils} is a collection of utility methods that complements
|
||||
* the standard support already available in {@link AnnotationUtils}.
|
||||
@@ -36,24 +34,26 @@ import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
* <p>Whereas {@code AnnotationUtils} provides utilities for <em>getting</em> or
|
||||
* <em>finding</em> an annotation, {@code MetaAnnotationUtils} goes a step further
|
||||
* by providing support for determining the <em>root class</em> on which an
|
||||
* annotation is declared, either directly or via a <em>composed annotation</em>.
|
||||
* This additional information is encapsulated in an {@link AnnotationDescriptor}.
|
||||
* annotation is declared, either directly or indirectly via a <em>composed
|
||||
* annotation</em>. This additional information is encapsulated in an
|
||||
* {@link AnnotationDescriptor}.
|
||||
*
|
||||
* <p>The additional information provided by an {@code AnnotationDescriptor} is
|
||||
* required by the <em>Spring TestContext Framework</em> in order to be able to
|
||||
* support class hierarchy traversals for annotations such as
|
||||
* {@link ContextConfiguration @ContextConfiguration},
|
||||
* {@link TestExecutionListeners @TestExecutionListeners}, and
|
||||
* {@link ActiveProfiles @ActiveProfiles} which offer support for merging and
|
||||
* overriding various <em>inherited</em> annotation attributes (e.g., {@link
|
||||
* ContextConfiguration#inheritLocations}).
|
||||
* {@link org.springframework.test.context.ContextConfiguration @ContextConfiguration},
|
||||
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners},
|
||||
* and {@link org.springframework.test.context.ActiveProfiles @ActiveProfiles}
|
||||
* which offer support for merging and overriding various <em>inherited</em>
|
||||
* annotation attributes (e.g., {@link
|
||||
* org.springframework.test.context.ContextConfiguration#inheritLocations}).
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.0
|
||||
* @see AnnotationUtils
|
||||
* @see AnnotationDescriptor
|
||||
*/
|
||||
abstract class MetaAnnotationUtils {
|
||||
public abstract class MetaAnnotationUtils {
|
||||
|
||||
private MetaAnnotationUtils() {
|
||||
/* no-op */
|
||||
@@ -117,13 +117,13 @@ abstract class MetaAnnotationUtils {
|
||||
}
|
||||
|
||||
// Declared locally?
|
||||
if (isAnnotationDeclaredLocally(annotationType, clazz)) {
|
||||
if (AnnotationUtils.isAnnotationDeclaredLocally(annotationType, clazz)) {
|
||||
return new AnnotationDescriptor<T>(clazz, clazz.getAnnotation(annotationType));
|
||||
}
|
||||
|
||||
// Declared on a composed annotation (i.e., as a meta-annotation)?
|
||||
for (Annotation composedAnnotation : clazz.getDeclaredAnnotations()) {
|
||||
if (!isInJavaLangAnnotationPackage(composedAnnotation) && visited.add(composedAnnotation)) {
|
||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(composedAnnotation) && visited.add(composedAnnotation)) {
|
||||
AnnotationDescriptor<T> descriptor = findAnnotationDescriptor(composedAnnotation.annotationType(),
|
||||
visited, annotationType);
|
||||
if (descriptor != null) {
|
||||
@@ -203,14 +203,14 @@ abstract class MetaAnnotationUtils {
|
||||
|
||||
// Declared locally?
|
||||
for (Class<? extends Annotation> annotationType : annotationTypes) {
|
||||
if (isAnnotationDeclaredLocally(annotationType, clazz)) {
|
||||
if (AnnotationUtils.isAnnotationDeclaredLocally(annotationType, clazz)) {
|
||||
return new UntypedAnnotationDescriptor(clazz, clazz.getAnnotation(annotationType));
|
||||
}
|
||||
}
|
||||
|
||||
// Declared on a composed annotation (i.e., as a meta-annotation)?
|
||||
for (Annotation composedAnnotation : clazz.getDeclaredAnnotations()) {
|
||||
if (!isInJavaLangAnnotationPackage(composedAnnotation) && visited.add(composedAnnotation)) {
|
||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(composedAnnotation) && visited.add(composedAnnotation)) {
|
||||
UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(
|
||||
composedAnnotation.annotationType(), visited, annotationTypes);
|
||||
if (descriptor != null) {
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Collection of test-related utility methods for working with {@link BootstrapContext
|
||||
* BootstrapContexts} and {@link TestContextBootstrapper TestContextBootstrappers}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
*/
|
||||
public abstract class BootstrapTestUtils {
|
||||
|
||||
private BootstrapTestUtils() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
public static BootstrapContext buildBootstrapContext(Class<?> testClass,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
|
||||
return new DefaultBootstrapContext(testClass, cacheAwareContextLoaderDelegate);
|
||||
}
|
||||
|
||||
public static TestContextBootstrapper resolveTestContextBootstrapper(BootstrapContext bootstrapContext) {
|
||||
return BootstrapUtils.resolveTestContextBootstrapper(bootstrapContext);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -56,7 +56,7 @@ public class ContextCacheTests {
|
||||
}
|
||||
|
||||
private ApplicationContext loadContext(Class<?> testClass) {
|
||||
TestContext testContext = new DefaultTestContext(testClass, contextCache);
|
||||
TestContext testContext = TestContextTestUtils.buildTestContext(testClass, contextCache);
|
||||
return testContext.getApplicationContext();
|
||||
}
|
||||
|
||||
@@ -119,15 +119,15 @@ public class ContextCacheTests {
|
||||
public void removeContextHierarchyCacheLevel1() {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = new DefaultTestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class,
|
||||
contextCache);
|
||||
TestContext testContext3a = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = new DefaultTestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class,
|
||||
contextCache);
|
||||
TestContext testContext3b = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
@@ -144,15 +144,15 @@ public class ContextCacheTests {
|
||||
public void removeContextHierarchyCacheLevel1WithExhaustiveMode() {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = new DefaultTestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class,
|
||||
contextCache);
|
||||
TestContext testContext3a = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = new DefaultTestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class,
|
||||
contextCache);
|
||||
TestContext testContext3b = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
@@ -169,15 +169,15 @@ public class ContextCacheTests {
|
||||
public void removeContextHierarchyCacheLevel2() {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = new DefaultTestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class,
|
||||
contextCache);
|
||||
TestContext testContext3a = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = new DefaultTestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class,
|
||||
contextCache);
|
||||
TestContext testContext3b = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
@@ -195,15 +195,15 @@ public class ContextCacheTests {
|
||||
public void removeContextHierarchyCacheLevel2WithExhaustiveMode() {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = new DefaultTestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class,
|
||||
contextCache);
|
||||
TestContext testContext3a = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = new DefaultTestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class,
|
||||
contextCache);
|
||||
TestContext testContext3b = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
@@ -219,15 +219,15 @@ public class ContextCacheTests {
|
||||
public void removeContextHierarchyCacheLevel3Then2() {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = new DefaultTestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class,
|
||||
contextCache);
|
||||
TestContext testContext3a = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = new DefaultTestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class,
|
||||
contextCache);
|
||||
TestContext testContext3b = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
@@ -248,15 +248,15 @@ public class ContextCacheTests {
|
||||
public void removeContextHierarchyCacheLevel3Then2WithExhaustiveMode() {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = new DefaultTestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class,
|
||||
contextCache);
|
||||
TestContext testContext3a = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = new DefaultTestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class,
|
||||
contextCache);
|
||||
TestContext testContext3b = TestContextTestUtils.buildTestContext(
|
||||
ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Collection of test-related utility methods for working with {@link TestContext TestContexts}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
*/
|
||||
public abstract class TestContextTestUtils {
|
||||
|
||||
private TestContextTestUtils() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
public static TestContext buildTestContext(Class<?> testClass, ContextCache contextCache) {
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = new DefaultCacheAwareContextLoaderDelegate(
|
||||
contextCache);
|
||||
return buildTestContext(testClass, null, cacheAwareContextLoaderDelegate);
|
||||
}
|
||||
|
||||
public static TestContext buildTestContext(Class<?> testClass, String customDefaultContextLoaderClassName,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
|
||||
BootstrapContext bootstrapContext = new DefaultBootstrapContext(testClass, cacheAwareContextLoaderDelegate);
|
||||
TestContextBootstrapper testContextBootstrapper = BootstrapUtils.resolveTestContextBootstrapper(bootstrapContext);
|
||||
|
||||
return new DefaultTestContext(testContextBootstrapper);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -45,7 +45,7 @@ public class TestExecutionListenersTests {
|
||||
@Test
|
||||
public void verifyNumDefaultListenersRegistered() throws Exception {
|
||||
TestContextManager testContextManager = new TestContextManager(DefaultListenersExampleTestCase.class);
|
||||
assertEquals("Num registered TELs for DefaultListenersExampleTestCase.", 4,
|
||||
assertEquals("Num registered TELs for DefaultListenersExampleTestCase.", 3,
|
||||
testContextManager.getTestExecutionListeners().size());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,27 +16,29 @@
|
||||
|
||||
package org.springframework.test.context.junit4;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.model.InitializationError;
|
||||
import org.springframework.tests.sample.beans.Pet;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.BootstrapWith;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.support.DefaultTestContextBootstrapper;
|
||||
import org.springframework.test.context.support.GenericPropertiesContextLoader;
|
||||
import org.springframework.tests.sample.beans.Pet;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Integration tests which verify that a subclass of {@link SpringJUnit4ClassRunner}
|
||||
* can specify a custom <em>default ContextLoader class name</em> that overrides
|
||||
* the standard default class name.
|
||||
* Integration tests which verify that a subclass of {@link DefaultTestContextBootstrapper}
|
||||
* can specify a custom <em>default ContextLoader class</em> that overrides the standard
|
||||
* default class name.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
@RunWith(CustomDefaultContextLoaderClassSpringRunnerTests.PropertiesBasedSpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = "PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests-context.properties")
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@BootstrapWith(CustomDefaultContextLoaderClassSpringRunnerTests.PropertiesBasedTestContextBootstrapper.class)
|
||||
@ContextConfiguration("PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests-context.properties")
|
||||
public class CustomDefaultContextLoaderClassSpringRunnerTests {
|
||||
|
||||
@Autowired
|
||||
@@ -56,16 +58,12 @@ public class CustomDefaultContextLoaderClassSpringRunnerTests {
|
||||
}
|
||||
|
||||
|
||||
public static final class PropertiesBasedSpringJUnit4ClassRunner extends SpringJUnit4ClassRunner {
|
||||
|
||||
public PropertiesBasedSpringJUnit4ClassRunner(Class<?> clazz) throws InitializationError {
|
||||
super(clazz);
|
||||
}
|
||||
public static class PropertiesBasedTestContextBootstrapper extends DefaultTestContextBootstrapper {
|
||||
|
||||
@Override
|
||||
protected String getDefaultContextLoaderClassName(Class<?> clazz) {
|
||||
return GenericPropertiesContextLoader.class.getName();
|
||||
protected Class<? extends ContextLoader> getDefaultContextLoaderClass(Class<?> testClass) {
|
||||
return GenericPropertiesContextLoader.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context;
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -23,10 +23,19 @@ import java.lang.annotation.Target;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.BootstrapContext;
|
||||
import org.springframework.test.context.BootstrapTestUtils;
|
||||
import org.springframework.test.context.CacheAwareContextLoaderDelegate;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.TestContextBootstrapper;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@@ -44,6 +53,14 @@ abstract class AbstractContextLoaderUtilsTests {
|
||||
Collections.<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> emptySet();
|
||||
|
||||
|
||||
MergedContextConfiguration buildMergedContextConfiguration(Class<?> testClass) {
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = Mockito.mock(CacheAwareContextLoaderDelegate.class);
|
||||
BootstrapContext bootstrapContext = BootstrapTestUtils.buildBootstrapContext(testClass,
|
||||
cacheAwareContextLoaderDelegate);
|
||||
TestContextBootstrapper bootstrapper = BootstrapTestUtils.resolveTestContextBootstrapper(bootstrapContext);
|
||||
return bootstrapper.buildMergedContextConfiguration();
|
||||
}
|
||||
|
||||
void assertAttributes(ContextConfigurationAttributes attributes, Class<?> expectedDeclaringClass,
|
||||
String[] expectedLocations, Class<?>[] expectedClasses,
|
||||
Class<? extends ContextLoader> expectedContextLoaderClass, boolean expectedInheritLocations) {
|
||||
@@ -173,4 +190,16 @@ abstract class AbstractContextLoaderUtilsTests {
|
||||
static class OverriddenClassesBar extends ClassesFoo {
|
||||
}
|
||||
|
||||
@ContextConfiguration(locations = "/foo.properties", loader = GenericPropertiesContextLoader.class)
|
||||
@ActiveProfiles(profiles = "foo")
|
||||
static class PropertiesLocationsFoo {
|
||||
}
|
||||
|
||||
// Combining @Configuration classes with a Properties based loader doesn't really make
|
||||
// sense, but that's OK for unit testing purposes.
|
||||
@ContextConfiguration(classes = FooConfig.class, loader = GenericPropertiesContextLoader.class)
|
||||
@ActiveProfiles(profiles = "foo")
|
||||
static class PropertiesClassesFoo {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context;
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -25,19 +25,21 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.ActiveProfilesResolver;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.context.ContextLoaderUtils.*;
|
||||
import static org.springframework.test.context.support.ActiveProfilesUtils.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ContextLoaderUtils} involving resolution of active bean
|
||||
* Unit tests for {@link ActiveProfilesUtils} involving resolution of active bean
|
||||
* definition profiles.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Michail Nikolaev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ContextLoaderUtilsActiveProfilesTests extends AbstractContextLoaderUtilsTests {
|
||||
public class ActiveProfilesUtilsTests extends AbstractContextLoaderUtilsTests {
|
||||
|
||||
private void assertResolvedProfiles(Class<?> testClass, String... expected) {
|
||||
assertNotNull(testClass);
|
||||
@@ -168,17 +170,17 @@ public class ContextLoaderUtilsActiveProfilesTests extends AbstractContextLoader
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void resolveActiveProfilesWithConflictingResolverAndProfiles() {
|
||||
resolveActiveProfiles(ConflictingResolverAndProfilesTestCase.class);
|
||||
@Test
|
||||
public void resolveActiveProfilesWithResolverAndProfiles() {
|
||||
assertResolvedProfiles(ResolverAndProfilesTestCase.class, "bar");
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void resolveActiveProfilesWithConflictingResolverAndValue() {
|
||||
resolveActiveProfiles(ConflictingResolverAndValueTestCase.class);
|
||||
@Test
|
||||
public void resolveActiveProfilesWithResolverAndValue() {
|
||||
assertResolvedProfiles(ResolverAndValueTestCase.class, "bar");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,19 +281,19 @@ public class ContextLoaderUtilsActiveProfilesTests extends AbstractContextLoader
|
||||
InheritedFooActiveProfilesResolverTestCase {
|
||||
}
|
||||
|
||||
@ActiveProfiles(resolver = BarActiveProfilesResolver.class, profiles = "conflict")
|
||||
private static class ConflictingResolverAndProfilesTestCase {
|
||||
@ActiveProfiles(resolver = BarActiveProfilesResolver.class, profiles = "ignored by custom resolver")
|
||||
private static class ResolverAndProfilesTestCase {
|
||||
}
|
||||
|
||||
@ActiveProfiles(resolver = BarActiveProfilesResolver.class, value = "conflict")
|
||||
private static class ConflictingResolverAndValueTestCase {
|
||||
@ActiveProfiles(resolver = BarActiveProfilesResolver.class, value = "ignored by custom resolver")
|
||||
private static class ResolverAndValueTestCase {
|
||||
}
|
||||
|
||||
@MetaResolverConfig
|
||||
private static class TestClassVerifyingActiveProfilesResolverTestCase {
|
||||
}
|
||||
|
||||
@ActiveProfiles(profiles = "conflict", value = "conflict")
|
||||
@ActiveProfiles(profiles = "conflict 1", value = "conflict 2")
|
||||
private static class ConflictingProfilesAndValueTestCase {
|
||||
}
|
||||
|
||||
@@ -14,15 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context;
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
import org.springframework.test.context.support.ContextLoaderUtils;
|
||||
|
||||
import static org.springframework.test.context.support.ContextLoaderUtils.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.context.ContextLoaderUtils.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ContextLoaderUtils} involving {@link ContextConfigurationAttributes}.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context;
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -25,11 +25,16 @@ import java.util.Map;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
import org.springframework.test.context.support.ContextLoaderUtils;
|
||||
|
||||
import static org.springframework.test.context.support.ContextLoaderUtils.*;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.context.ContextLoaderUtils.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ContextLoaderUtils} involving context hierarchies.
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context;
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -23,11 +23,10 @@ import org.junit.Test;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.test.context.support.DelegatingSmartContextLoader;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
|
||||
import static org.springframework.test.context.ContextLoaderUtils.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ContextLoaderUtils} involving {@link ApplicationContextInitializer}s.
|
||||
*
|
||||
@@ -44,7 +43,8 @@ public class ContextLoaderUtilsContextInitializerTests extends AbstractContextLo
|
||||
= new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
expectedInitializerClasses.add(FooInitializer.class);
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
|
||||
|
||||
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
|
||||
DelegatingSmartContextLoader.class);
|
||||
}
|
||||
@@ -58,7 +58,8 @@ public class ContextLoaderUtilsContextInitializerTests extends AbstractContextLo
|
||||
expectedInitializerClasses.add(FooInitializer.class);
|
||||
expectedInitializerClasses.add(BarInitializer.class);
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
|
||||
|
||||
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
|
||||
DelegatingSmartContextLoader.class);
|
||||
}
|
||||
@@ -71,7 +72,8 @@ public class ContextLoaderUtilsContextInitializerTests extends AbstractContextLo
|
||||
= new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
expectedInitializerClasses.add(BarInitializer.class);
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
|
||||
|
||||
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
|
||||
DelegatingSmartContextLoader.class);
|
||||
}
|
||||
@@ -84,7 +86,8 @@ public class ContextLoaderUtilsContextInitializerTests extends AbstractContextLo
|
||||
= new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
expectedInitializerClasses.add(BarInitializer.class);
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
|
||||
|
||||
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
|
||||
DelegatingSmartContextLoader.class);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,14 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context;
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
import org.springframework.test.context.support.DelegatingSmartContextLoader;
|
||||
import org.springframework.test.context.support.GenericPropertiesContextLoader;
|
||||
|
||||
import static org.springframework.test.context.ContextLoaderUtils.*;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ContextLoaderUtils} involving {@link MergedContextConfiguration}.
|
||||
@@ -33,28 +30,28 @@ public class ContextLoaderUtilsMergedConfigTests extends AbstractContextLoaderUt
|
||||
|
||||
@Test
|
||||
public void buildMergedConfigWithoutAnnotation() {
|
||||
Class<Enigma> testClass = Enigma.class;
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
Class<?> testClass = Enigma.class;
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
|
||||
|
||||
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildMergedConfigWithBareAnnotations() {
|
||||
Class<BareAnnotations> testClass = BareAnnotations.class;
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
Class<?> testClass = BareAnnotations.class;
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
|
||||
|
||||
assertMergedConfig(
|
||||
mergedConfig,
|
||||
testClass,
|
||||
new String[] { "classpath:/org/springframework/test/context/AbstractContextLoaderUtilsTests$BareAnnotations-context.xml" },
|
||||
new String[] { "classpath:/org/springframework/test/context/support/AbstractContextLoaderUtilsTests$BareAnnotations-context.xml" },
|
||||
EMPTY_CLASS_ARRAY, DelegatingSmartContextLoader.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildMergedConfigWithLocalAnnotationAndLocations() {
|
||||
Class<?> testClass = LocationsFoo.class;
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
|
||||
|
||||
assertMergedConfig(mergedConfig, testClass, new String[] { "classpath:/foo.xml" }, EMPTY_CLASS_ARRAY,
|
||||
DelegatingSmartContextLoader.class);
|
||||
@@ -63,7 +60,7 @@ public class ContextLoaderUtilsMergedConfigTests extends AbstractContextLoaderUt
|
||||
@Test
|
||||
public void buildMergedConfigWithMetaAnnotationAndLocations() {
|
||||
Class<?> testClass = MetaLocationsFoo.class;
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
|
||||
|
||||
assertMergedConfig(mergedConfig, testClass, new String[] { "classpath:/foo.xml" }, EMPTY_CLASS_ARRAY,
|
||||
DelegatingSmartContextLoader.class);
|
||||
@@ -72,7 +69,7 @@ public class ContextLoaderUtilsMergedConfigTests extends AbstractContextLoaderUt
|
||||
@Test
|
||||
public void buildMergedConfigWithLocalAnnotationAndClasses() {
|
||||
Class<?> testClass = ClassesFoo.class;
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
|
||||
|
||||
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, new Class<?>[] { FooConfig.class },
|
||||
DelegatingSmartContextLoader.class);
|
||||
@@ -80,21 +77,19 @@ public class ContextLoaderUtilsMergedConfigTests extends AbstractContextLoaderUt
|
||||
|
||||
@Test
|
||||
public void buildMergedConfigWithLocalAnnotationAndOverriddenContextLoaderAndLocations() {
|
||||
Class<?> testClass = LocationsFoo.class;
|
||||
Class<?> testClass = PropertiesLocationsFoo.class;
|
||||
Class<? extends ContextLoader> expectedContextLoaderClass = GenericPropertiesContextLoader.class;
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass,
|
||||
expectedContextLoaderClass.getName(), null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
|
||||
|
||||
assertMergedConfig(mergedConfig, testClass, new String[] { "classpath:/foo.xml" }, EMPTY_CLASS_ARRAY,
|
||||
assertMergedConfig(mergedConfig, testClass, new String[] { "classpath:/foo.properties" }, EMPTY_CLASS_ARRAY,
|
||||
expectedContextLoaderClass);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildMergedConfigWithLocalAnnotationAndOverriddenContextLoaderAndClasses() {
|
||||
Class<?> testClass = ClassesFoo.class;
|
||||
Class<?> testClass = PropertiesClassesFoo.class;
|
||||
Class<? extends ContextLoader> expectedContextLoaderClass = GenericPropertiesContextLoader.class;
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass,
|
||||
expectedContextLoaderClass.getName(), null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
|
||||
|
||||
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, new Class<?>[] { FooConfig.class },
|
||||
expectedContextLoaderClass);
|
||||
@@ -104,8 +99,8 @@ public class ContextLoaderUtilsMergedConfigTests extends AbstractContextLoaderUt
|
||||
public void buildMergedConfigWithLocalAndInheritedAnnotationsAndLocations() {
|
||||
Class<?> testClass = LocationsBar.class;
|
||||
String[] expectedLocations = new String[] { "/foo.xml", "/bar.xml" };
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
assertMergedConfig(mergedConfig, testClass, expectedLocations, EMPTY_CLASS_ARRAY,
|
||||
AnnotationConfigContextLoader.class);
|
||||
}
|
||||
@@ -114,8 +109,8 @@ public class ContextLoaderUtilsMergedConfigTests extends AbstractContextLoaderUt
|
||||
public void buildMergedConfigWithLocalAndInheritedAnnotationsAndClasses() {
|
||||
Class<?> testClass = ClassesBar.class;
|
||||
Class<?>[] expectedClasses = new Class<?>[] { FooConfig.class, BarConfig.class };
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses,
|
||||
AnnotationConfigContextLoader.class);
|
||||
}
|
||||
@@ -124,8 +119,8 @@ public class ContextLoaderUtilsMergedConfigTests extends AbstractContextLoaderUt
|
||||
public void buildMergedConfigWithAnnotationsAndOverriddenLocations() {
|
||||
Class<?> testClass = OverriddenLocationsBar.class;
|
||||
String[] expectedLocations = new String[] { "/bar.xml" };
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
assertMergedConfig(mergedConfig, testClass, expectedLocations, EMPTY_CLASS_ARRAY,
|
||||
AnnotationConfigContextLoader.class);
|
||||
}
|
||||
@@ -134,8 +129,8 @@ public class ContextLoaderUtilsMergedConfigTests extends AbstractContextLoaderUt
|
||||
public void buildMergedConfigWithAnnotationsAndOverriddenClasses() {
|
||||
Class<?> testClass = OverriddenClassesBar.class;
|
||||
Class<?>[] expectedClasses = new Class<?>[] { BarConfig.class };
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses,
|
||||
AnnotationConfigContextLoader.class);
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context;
|
||||
package org.springframework.test.util;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -27,12 +27,15 @@ import org.junit.Test;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.test.context.MetaAnnotationUtils.AnnotationDescriptor;
|
||||
import org.springframework.test.context.MetaAnnotationUtils.UntypedAnnotationDescriptor;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.util.MetaAnnotationUtils;
|
||||
import org.springframework.test.util.MetaAnnotationUtils.AnnotationDescriptor;
|
||||
import org.springframework.test.util.MetaAnnotationUtils.UntypedAnnotationDescriptor;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import static org.springframework.test.util.MetaAnnotationUtils.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.context.MetaAnnotationUtils.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MetaAnnotationUtils}.
|
||||
@@ -14,17 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context;
|
||||
package org.springframework.test.util;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.test.context.MetaAnnotationUtils.AnnotationDescriptor;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.util.MetaAnnotationUtils;
|
||||
import org.springframework.test.util.MetaAnnotationUtils.AnnotationDescriptor;
|
||||
|
||||
import static org.springframework.test.util.MetaAnnotationUtils.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.context.MetaAnnotationUtils.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MetaAnnotationUtils} that verify support for overridden
|
||||
Reference in New Issue
Block a user