Merge pull request #991 from philwebb/SPR-13998
* SPR-13998: Polish ContextCustomizer support in the TCF Introduce ContextCustomizer API in the TestContext Framework
This commit is contained in:
commit
beb01e674e
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2002-2016 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.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
* Strategy interface for customizing {@link ConfigurableApplicationContext
|
||||
* application contexts} that are created and managed by the <em>Spring
|
||||
* TestContext Framework</em>.
|
||||
*
|
||||
* <p>Customizers are created by {@link ContextCustomizerFactory} implementations.
|
||||
*
|
||||
* <p>Implementations must implement correct {@code equals} and {@code hashCode}
|
||||
* methods since customizers form part of the {@link MergedContextConfiguration}
|
||||
* which is used as a cache key.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 4.3
|
||||
* @see ContextCustomizerFactory
|
||||
* @see org.springframework.test.context.support.AbstractContextLoader#customizeContext
|
||||
*/
|
||||
public interface ContextCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the supplied {@code ConfigurableApplicationContext} <em>after</em>
|
||||
* bean definitions have been loaded into the context but <em>before</em> the
|
||||
* context has been refreshed.
|
||||
* @param context the context to customize
|
||||
* @param mergedConfig the merged context configuration
|
||||
*/
|
||||
void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2002-2016 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;
|
||||
|
||||
/**
|
||||
* Factory for creating {@link ContextCustomizer ContextCustomizers}.
|
||||
*
|
||||
* <p>Factories are invoked after {@link ContextLoader ContextLoaders} have
|
||||
* processed context configuration attributes but before the
|
||||
* {@link MergedContextConfiguration} is created.
|
||||
*
|
||||
* <p>By default, the Spring TestContext Framework will use the
|
||||
* {@link org.springframework.core.io.support.SpringFactoriesLoader SpringFactoriesLoader}
|
||||
* mechanism for loading factories configured in all {@code META-INF/spring.factories}
|
||||
* files on the classpath.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 4.3
|
||||
*/
|
||||
public interface ContextCustomizerFactory {
|
||||
|
||||
/**
|
||||
* Create a {@link ContextCustomizer} that should be used to customize a
|
||||
* {@link org.springframework.context.ConfigurableApplicationContext ConfigurableApplicationContext}
|
||||
* before it is refreshed.
|
||||
* @param testClass the test class
|
||||
* @param configAttributes the list of context configuration attributes for
|
||||
* the test class, ordered <em>bottom-up</em> (i.e., as if we were traversing
|
||||
* up the class hierarchy); never {@code null} or empty
|
||||
* @return a {@link ContextCustomizer} or {@code null} if no customizer should
|
||||
* be used
|
||||
*/
|
||||
ContextCustomizer createContextCustomizer(Class<?> testClass, List<ContextConfigurationAttributes> configAttributes);
|
||||
|
||||
}
|
||||
|
|
@ -55,6 +55,7 @@ import org.springframework.util.StringUtils;
|
|||
* that was loaded using properties of this {@code MergedContextConfiguration}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Phillip Webb
|
||||
* @since 3.1
|
||||
* @see ContextConfiguration
|
||||
* @see ContextHierarchy
|
||||
|
|
@ -74,6 +75,8 @@ public class MergedContextConfiguration implements Serializable {
|
|||
private static final Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> EMPTY_INITIALIZER_CLASSES =
|
||||
Collections.<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> emptySet();
|
||||
|
||||
private static final Set<ContextCustomizer> EMPTY_CONTEXT_CUSTOMIZERS = Collections.<ContextCustomizer> emptySet();
|
||||
|
||||
|
||||
private final Class<?> testClass;
|
||||
|
||||
|
|
@ -89,6 +92,8 @@ public class MergedContextConfiguration implements Serializable {
|
|||
|
||||
private final String[] propertySourceProperties;
|
||||
|
||||
private final Set<ContextCustomizer> contextCustomizers;
|
||||
|
||||
private final ContextLoader contextLoader;
|
||||
|
||||
private final CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate;
|
||||
|
|
@ -111,6 +116,11 @@ public class MergedContextConfiguration implements Serializable {
|
|||
Collections.unmodifiableSet(contextInitializerClasses) : EMPTY_INITIALIZER_CLASSES);
|
||||
}
|
||||
|
||||
private static Set<ContextCustomizer> processContextCustomizers(Set<ContextCustomizer> contextCustomizers) {
|
||||
return (contextCustomizers != null ?
|
||||
Collections.unmodifiableSet(contextCustomizers) : EMPTY_CONTEXT_CUSTOMIZERS);
|
||||
}
|
||||
|
||||
private static String[] processActiveProfiles(String[] activeProfiles) {
|
||||
if (activeProfiles == null) {
|
||||
return EMPTY_STRING_ARRAY;
|
||||
|
|
@ -201,8 +211,8 @@ public class MergedContextConfiguration implements Serializable {
|
|||
public MergedContextConfiguration(MergedContextConfiguration mergedConfig) {
|
||||
this(mergedConfig.testClass, mergedConfig.locations, mergedConfig.classes,
|
||||
mergedConfig.contextInitializerClasses, mergedConfig.activeProfiles, mergedConfig.propertySourceLocations,
|
||||
mergedConfig.propertySourceProperties, mergedConfig.contextLoader,
|
||||
mergedConfig.cacheAwareContextLoaderDelegate, mergedConfig.parent);
|
||||
mergedConfig.propertySourceProperties, mergedConfig.contextCustomizers,
|
||||
mergedConfig.contextLoader, mergedConfig.cacheAwareContextLoaderDelegate, mergedConfig.parent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -233,6 +243,41 @@ public class MergedContextConfiguration implements Serializable {
|
|||
String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
|
||||
ContextLoader contextLoader, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate,
|
||||
MergedContextConfiguration parent) {
|
||||
this(testClass, locations, classes, contextInitializerClasses, activeProfiles,
|
||||
propertySourceLocations, propertySourceProperties,
|
||||
Collections.<ContextCustomizer> emptySet(), contextLoader,
|
||||
cacheAwareContextLoaderDelegate, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code MergedContextConfiguration} instance for the
|
||||
* supplied parameters.
|
||||
* <p>If a {@code null} value is supplied for {@code locations},
|
||||
* {@code classes}, {@code activeProfiles}, {@code propertySourceLocations},
|
||||
* or {@code propertySourceProperties} an empty array will be stored instead.
|
||||
* If a {@code null} value is supplied for {@code contextInitializerClasses}
|
||||
* or {@code contextCustomizers}, an empty set will be stored instead.
|
||||
* Furthermore, active profiles will be sorted, and duplicate profiles
|
||||
* will be removed.
|
||||
* @param testClass the test class for which the configuration was merged
|
||||
* @param locations the merged context resource locations
|
||||
* @param classes the merged annotated classes
|
||||
* @param contextInitializerClasses the merged context initializer classes
|
||||
* @param activeProfiles the merged active bean definition profiles
|
||||
* @param propertySourceLocations the merged {@code PropertySource} locations
|
||||
* @param propertySourceProperties the merged {@code PropertySource} properties
|
||||
* @param contextCustomizers the context customizers
|
||||
* @param contextLoader the resolved {@code ContextLoader}
|
||||
* @param cacheAwareContextLoaderDelegate a cache-aware context loader
|
||||
* delegate with which to retrieve the parent context
|
||||
* @param parent the parent configuration or {@code null} if there is no parent
|
||||
* @since 4.3
|
||||
*/
|
||||
public MergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
|
||||
String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
|
||||
Set<ContextCustomizer> contextCustomizers, ContextLoader contextLoader,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parent) {
|
||||
|
||||
this.testClass = testClass;
|
||||
this.locations = processStrings(locations);
|
||||
|
|
@ -241,6 +286,7 @@ public class MergedContextConfiguration implements Serializable {
|
|||
this.activeProfiles = processActiveProfiles(activeProfiles);
|
||||
this.propertySourceLocations = processStrings(propertySourceLocations);
|
||||
this.propertySourceProperties = processStrings(propertySourceProperties);
|
||||
this.contextCustomizers = processContextCustomizers(contextCustomizers);
|
||||
this.contextLoader = contextLoader;
|
||||
this.cacheAwareContextLoaderDelegate = cacheAwareContextLoaderDelegate;
|
||||
this.parent = parent;
|
||||
|
|
@ -348,6 +394,14 @@ public class MergedContextConfiguration implements Serializable {
|
|||
return this.propertySourceProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the merged {@link ContextCustomizer ContextCustomizers} that will be applied
|
||||
* when the application context is loaded.
|
||||
*/
|
||||
public Set<ContextCustomizer> getContextCustomizers() {
|
||||
return this.contextCustomizers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resolved {@link ContextLoader} for the {@linkplain #getTestClass() test class}.
|
||||
*/
|
||||
|
|
@ -424,6 +478,9 @@ public class MergedContextConfiguration implements Serializable {
|
|||
if (!Arrays.equals(this.propertySourceProperties, otherConfig.propertySourceProperties)) {
|
||||
return false;
|
||||
}
|
||||
if (!this.contextCustomizers.equals(otherConfig.contextCustomizers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.parent == null) {
|
||||
if (otherConfig.parent != null) {
|
||||
|
|
@ -454,6 +511,7 @@ public class MergedContextConfiguration implements Serializable {
|
|||
result = 31 * result + Arrays.hashCode(this.activeProfiles);
|
||||
result = 31 * result + Arrays.hashCode(this.propertySourceLocations);
|
||||
result = 31 * result + Arrays.hashCode(this.propertySourceProperties);
|
||||
result = 31 * result + this.contextCustomizers.hashCode();
|
||||
result = 31 * result + (this.parent != null ? this.parent.hashCode() : 0);
|
||||
result = 31 * result + nullSafeToString(this.contextLoader).hashCode();
|
||||
return result;
|
||||
|
|
@ -466,6 +524,7 @@ public class MergedContextConfiguration implements Serializable {
|
|||
* {@linkplain #getActiveProfiles() active profiles},
|
||||
* {@linkplain #getPropertySourceLocations() property source locations},
|
||||
* {@linkplain #getPropertySourceProperties() property source properties},
|
||||
* {@linkplain #getContextCustomizers() context customizers},
|
||||
* the name of the {@link #getContextLoader() ContextLoader}, and the
|
||||
* {@linkplain #getParent() parent configuration}.
|
||||
*/
|
||||
|
|
@ -479,6 +538,7 @@ public class MergedContextConfiguration implements Serializable {
|
|||
.append("activeProfiles", ObjectUtils.nullSafeToString(this.activeProfiles))
|
||||
.append("propertySourceLocations", ObjectUtils.nullSafeToString(this.propertySourceLocations))
|
||||
.append("propertySourceProperties", ObjectUtils.nullSafeToString(this.propertySourceProperties))
|
||||
.append("contextCustomizers", this.contextCustomizers)
|
||||
.append("contextLoader", nullSafeToString(this.contextLoader))
|
||||
.append("parent", this.parent)
|
||||
.toString();
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
|||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.SmartContextLoader;
|
||||
|
|
@ -56,10 +57,13 @@ import org.springframework.util.ResourceUtils;
|
|||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
* @since 2.5
|
||||
* @see #generateDefaultLocations
|
||||
* @see #getResourceSuffixes
|
||||
* @see #modifyLocations
|
||||
* @see #prepareContext
|
||||
* @see #customizeContext
|
||||
*/
|
||||
public abstract class AbstractContextLoader implements SmartContextLoader {
|
||||
|
||||
|
|
@ -167,6 +171,23 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the {@link ConfigurableApplicationContext} created by this
|
||||
* {@code ContextLoader} <em>after</em> bean definitions have been loaded
|
||||
* into the context but <em>before</em> the context has been refreshed.
|
||||
* <p>The default implementation delegates to all
|
||||
* {@link MergedContextConfiguration#getContextCustomizers context customizers}
|
||||
* that have been registered with the supplied {@code mergedConfig}.
|
||||
* @param context the newly created application context
|
||||
* @param mergedConfig the merged context configuration
|
||||
* @since 4.3
|
||||
*/
|
||||
protected void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
|
||||
for (ContextCustomizer contextCustomizer : mergedConfig.getContextCustomizers()) {
|
||||
contextCustomizer.customizeContext(context, mergedConfig);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- ContextLoader -------------------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -253,8 +253,9 @@ public abstract class AbstractDelegatingSmartContextLoader implements SmartConte
|
|||
}
|
||||
|
||||
// If neither of the candidates supports the mergedConfig based on resources but
|
||||
// ACIs were declared, then delegate to the annotation config loader.
|
||||
if (!mergedConfig.getContextInitializerClasses().isEmpty()) {
|
||||
// ACIs or customizers were declared, then delegate to the annotation config
|
||||
// loader.
|
||||
if (!mergedConfig.getContextInitializerClasses().isEmpty() || !mergedConfig.getContextCustomizers().isEmpty()) {
|
||||
return delegateLoading(getAnnotationConfigLoader(), mergedConfig);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
|
|
@ -53,6 +52,7 @@ import org.springframework.util.StringUtils;
|
|||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
* @since 2.5
|
||||
* @see #loadContext(MergedContextConfiguration)
|
||||
* @see #loadContext(String...)
|
||||
|
|
@ -92,6 +92,8 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
|
|||
* annotation configuration processors.</li>
|
||||
* <li>Calls {@link #customizeContext(GenericApplicationContext)} to allow for customizing the context
|
||||
* before it is refreshed.</li>
|
||||
* <li>Calls {@link #customizeContext(ConfigurableApplicationContext, MergedContextConfiguration)} to
|
||||
* allow for customizing the context before it is refreshed.</li>
|
||||
* <li>{@link ConfigurableApplicationContext#refresh Refreshes} the
|
||||
* context and registers a JVM shutdown hook for it.</li>
|
||||
* </ul>
|
||||
|
|
@ -122,6 +124,7 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
|
|||
loadBeanDefinitions(context, mergedConfig);
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
customizeContext(context);
|
||||
customizeContext(context, mergedConfig);
|
||||
context.refresh();
|
||||
context.registerShutdownHook();
|
||||
return context;
|
||||
|
|
@ -205,6 +208,7 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
|
|||
* @see GenericApplicationContext#setAllowBeanDefinitionOverriding
|
||||
* @see GenericApplicationContext#setResourceLoader
|
||||
* @see GenericApplicationContext#setId
|
||||
* @see #prepareContext(ConfigurableApplicationContext, MergedContextConfiguration)
|
||||
* @since 2.5
|
||||
*/
|
||||
protected void prepareContext(GenericApplicationContext context) {
|
||||
|
|
@ -278,6 +282,7 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
|
|||
* @param context the newly created application context
|
||||
* @see #loadContext(MergedContextConfiguration)
|
||||
* @see #loadContext(String...)
|
||||
* @see #customizeContext(ConfigurableApplicationContext, MergedContextConfiguration)
|
||||
* @since 2.5
|
||||
*/
|
||||
protected void customizeContext(GenericApplicationContext context) {
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ 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.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
|
|
@ -385,10 +387,13 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||
}
|
||||
}
|
||||
|
||||
if (requireLocationsClassesOrInitializers && areAllEmpty(locations, classes, initializers)) {
|
||||
Set<ContextCustomizer> contextCustomizers = getContextCustomizers(testClass,
|
||||
Collections.unmodifiableList(configAttributesList));
|
||||
|
||||
if (requireLocationsClassesOrInitializers && areAllEmpty(locations, classes, initializers, contextCustomizers)) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"%s was unable to detect defaults, and no ApplicationContextInitializers "
|
||||
+ "were declared for context configuration attributes %s",
|
||||
+ "or ContextCustomizers were declared for context configuration attributes %s",
|
||||
contextLoader.getClass().getSimpleName(), configAttributesList));
|
||||
}
|
||||
|
||||
|
|
@ -400,11 +405,40 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||
ActiveProfilesUtils.resolveActiveProfiles(testClass),
|
||||
mergedTestPropertySources.getLocations(),
|
||||
mergedTestPropertySources.getProperties(),
|
||||
contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
|
||||
contextCustomizers, contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
|
||||
|
||||
return processMergedContextConfiguration(mergedConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
private Set<ContextCustomizer> getContextCustomizers(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
|
||||
List<ContextCustomizerFactory> factories = getContextCustomizerFactories();
|
||||
Set<ContextCustomizer> customizers = new LinkedHashSet<ContextCustomizer>(factories.size());
|
||||
for (ContextCustomizerFactory factory : factories) {
|
||||
ContextCustomizer customizer = factory.createContextCustomizer(testClass, configAttributes);
|
||||
if (customizer != null) {
|
||||
customizers.add(customizer);
|
||||
}
|
||||
}
|
||||
return customizers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ContextCustomizerFactory} instances for this bootstrapper.
|
||||
* <p>The default implementation uses the {@link SpringFactoriesLoader} mechanism
|
||||
* for loading factories configured in all {@code META-INF/spring.factories}
|
||||
* files on the classpath.
|
||||
* @since 4.3
|
||||
* @see SpringFactoriesLoader#loadFactories
|
||||
*/
|
||||
protected List<ContextCustomizerFactory> getContextCustomizerFactories() {
|
||||
return SpringFactoriesLoader.loadFactories(ContextCustomizerFactory.class, getClass().getClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
|
|
@ -53,6 +53,7 @@ import org.springframework.web.context.support.GenericWebApplicationContext;
|
|||
* {@link #loadBeanDefinitions}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Phillip Webb
|
||||
* @since 3.2
|
||||
* @see #loadContext(MergedContextConfiguration)
|
||||
* @see #loadContext(String...)
|
||||
|
|
@ -256,15 +257,17 @@ public abstract class AbstractGenericWebContextLoader extends AbstractContextLoa
|
|||
* loader <i>after</i> bean definitions have been loaded into the context but
|
||||
* <i>before</i> the context is refreshed.
|
||||
*
|
||||
* <p>The default implementation is empty but can be overridden in subclasses
|
||||
* to customize the web application context.
|
||||
* <p>The default implementation simply delegates to
|
||||
* {@link AbstractContextLoader#customizeContext(ConfigurableApplicationContext, MergedContextConfiguration)}.
|
||||
*
|
||||
* @param context the newly created web application context
|
||||
* @param webMergedConfig the merged context configuration to use to load the
|
||||
* web application context
|
||||
* @see #loadContext(MergedContextConfiguration)
|
||||
* @see #customizeContext(ConfigurableApplicationContext, MergedContextConfiguration)
|
||||
*/
|
||||
protected void customizeContext(GenericWebApplicationContext context, WebMergedContextConfiguration webMergedConfig) {
|
||||
super.customizeContext(context, webMergedConfig);
|
||||
}
|
||||
|
||||
// --- ContextLoader -------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import org.springframework.context.ApplicationContextInitializer;
|
|||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.test.context.CacheAwareContextLoaderDelegate;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
|
@ -132,12 +133,47 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
|||
String resourceBasePath, ContextLoader contextLoader,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parent) {
|
||||
|
||||
super(testClass, locations, classes, contextInitializerClasses, activeProfiles, propertySourceLocations,
|
||||
propertySourceProperties, contextLoader, cacheAwareContextLoaderDelegate, parent);
|
||||
|
||||
this.resourceBasePath = !StringUtils.hasText(resourceBasePath) ? "" : resourceBasePath;
|
||||
this(testClass, locations, classes, contextInitializerClasses, activeProfiles, propertySourceLocations,
|
||||
propertySourceProperties, null, resourceBasePath, contextLoader, cacheAwareContextLoaderDelegate, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code WebMergedContextConfiguration} instance for the
|
||||
* supplied parameters.
|
||||
* <p>If a {@code null} value is supplied for {@code locations},
|
||||
* {@code classes}, {@code activeProfiles}, {@code propertySourceLocations},
|
||||
* or {@code propertySourceProperties} an empty array will be stored instead.
|
||||
* If a {@code null} value is supplied for {@code contextInitializerClasses}
|
||||
* or {@code contextCustomizers}, an empty set will be stored instead.
|
||||
* If an <em>empty</em> value is supplied for the {@code resourceBasePath}
|
||||
* an empty string will be used. Furthermore, active profiles will be sorted,
|
||||
* and duplicate profiles will be removed.
|
||||
* @param testClass the test class for which the configuration was merged
|
||||
* @param locations the merged context resource locations
|
||||
* @param classes the merged annotated classes
|
||||
* @param contextInitializerClasses the merged context initializer classes
|
||||
* @param activeProfiles the merged active bean definition profiles
|
||||
* @param propertySourceLocations the merged {@code PropertySource} locations
|
||||
* @param propertySourceProperties the merged {@code PropertySource} properties
|
||||
* @param contextCustomizers the context customizers
|
||||
* @param resourceBasePath the resource path to the root directory of the web application
|
||||
* @param contextLoader the resolved {@code ContextLoader}
|
||||
* @param cacheAwareContextLoaderDelegate a cache-aware context loader
|
||||
* delegate with which to retrieve the parent context
|
||||
* @param parent the parent configuration or {@code null} if there is no parent
|
||||
* @since 4.3
|
||||
*/
|
||||
public WebMergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
|
||||
String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
|
||||
Set<ContextCustomizer> contextCustomizers, String resourceBasePath, ContextLoader contextLoader,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parent) {
|
||||
|
||||
super(testClass, locations, classes, contextInitializerClasses, activeProfiles, propertySourceLocations,
|
||||
propertySourceProperties, contextCustomizers, contextLoader, cacheAwareContextLoaderDelegate, parent);
|
||||
|
||||
this.resourceBasePath = (StringUtils.hasText(resourceBasePath) ? resourceBasePath : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource path to the root directory of the web application for the
|
||||
|
|
@ -182,6 +218,7 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
|||
* {@linkplain #getActiveProfiles() active profiles},
|
||||
* {@linkplain #getPropertySourceLocations() property source locations},
|
||||
* {@linkplain #getPropertySourceProperties() property source properties},
|
||||
* {@linkplain #getContextCustomizers() context customizers},
|
||||
* {@linkplain #getResourceBasePath() resource base path}, the name of the
|
||||
* {@link #getContextLoader() ContextLoader}, and the
|
||||
* {@linkplain #getParent() parent configuration}.
|
||||
|
|
@ -196,6 +233,7 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
|||
.append("activeProfiles", ObjectUtils.nullSafeToString(getActiveProfiles()))
|
||||
.append("propertySourceLocations", ObjectUtils.nullSafeToString(getPropertySourceLocations()))
|
||||
.append("propertySourceProperties", ObjectUtils.nullSafeToString(getPropertySourceProperties()))
|
||||
.append("contextCustomizers", getContextCustomizers())
|
||||
.append("resourceBasePath", getResourceBasePath())
|
||||
.append("contextLoader", nullSafeToString(getContextLoader()))
|
||||
.append("parent", getParent())
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.test.context;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
|
@ -28,6 +29,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
|||
import org.springframework.test.context.support.GenericXmlContextLoader;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MergedContextConfiguration}.
|
||||
|
|
@ -37,6 +39,7 @@ import static org.junit.Assert.*;
|
|||
* {@link org.springframework.test.context.cache.ContextCache ContextCache}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Phillip Webb
|
||||
* @since 3.1
|
||||
*/
|
||||
public class MergedContextConfigurationTests {
|
||||
|
|
@ -400,6 +403,35 @@ public class MergedContextConfigurationTests {
|
|||
assertNotEquals(mergedConfig2, mergedConfig1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
@Test
|
||||
public void equalsWithSameContextCustomizers() {
|
||||
Set<ContextCustomizer> customizers = Collections.singleton(mock(ContextCustomizer.class));
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, null, null, customizers, loader, null, null);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, null, null, customizers, loader, null, null);
|
||||
assertEquals(mergedConfig1, mergedConfig2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
@Test
|
||||
public void equalsWithDifferentContextCustomizers() {
|
||||
Set<ContextCustomizer> customizers1 = Collections.singleton(mock(ContextCustomizer.class));
|
||||
Set<ContextCustomizer> customizers2 = Collections.singleton(mock(ContextCustomizer.class));
|
||||
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, null, null, customizers1, loader, null, null);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, null, null, customizers2, loader, null, null);
|
||||
assertNotEquals(mergedConfig1, mergedConfig2);
|
||||
assertNotEquals(mergedConfig2, mergedConfig1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.2.2
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context.junit4;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.BootstrapWith;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
import org.springframework.test.context.junit4.ContextCustomizerSpringRunnerTests.CustomTestContextBootstrapper;
|
||||
import org.springframework.test.context.support.DefaultTestContextBootstrapper;
|
||||
|
||||
import static java.util.Collections.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* JUnit 4 based integration test which verifies support of
|
||||
* {@link ContextCustomizerFactory} and {@link ContextCustomizer}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Phillip Webb
|
||||
* @since 4.3
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@BootstrapWith(CustomTestContextBootstrapper.class)
|
||||
public class ContextCustomizerSpringRunnerTests {
|
||||
|
||||
@Autowired String foo;
|
||||
|
||||
|
||||
@Test
|
||||
public void injectedBean() {
|
||||
assertEquals("foo", foo);
|
||||
}
|
||||
|
||||
|
||||
static class CustomTestContextBootstrapper extends DefaultTestContextBootstrapper {
|
||||
|
||||
@Override
|
||||
protected List<ContextCustomizerFactory> getContextCustomizerFactories() {
|
||||
return singletonList((testClass, configAttributes) ->
|
||||
// ContextCustomizer as lambda expression:
|
||||
(context, mergedConfig) -> context.getBeanFactory().registerSingleton("foo", "foo"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ public class BootstrapTestUtilsMergedConfigTests extends AbstractContextConfigur
|
|||
public void buildMergedConfigWithContextConfigurationWithoutLocationsClassesOrInitializers() {
|
||||
exception.expect(IllegalStateException.class);
|
||||
exception.expectMessage(startsWith("DelegatingSmartContextLoader was unable to detect defaults, "
|
||||
+ "and no ApplicationContextInitializers were declared for context configuration attributes"));
|
||||
+ "and no ApplicationContextInitializers or ContextCustomizers were declared for context configuration attributes"));
|
||||
|
||||
buildMergedContextConfiguration(MissingContextAttributesTestCase.class);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue