Merge from sbrannen/SPR-11466
* SPR-11466: Support automatic discovery of default TELs
This commit is contained in:
commit
e753f23110
|
@ -27,7 +27,7 @@ import java.util.Properties;
|
|||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
@ -48,12 +48,13 @@ import org.springframework.util.StringUtils;
|
|||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
*/
|
||||
public abstract class SpringFactoriesLoader {
|
||||
|
||||
/** The location to look for the factories. Can be present in multiple JAR files. */
|
||||
private static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
|
||||
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
|
||||
|
||||
|
@ -61,7 +62,7 @@ public abstract class SpringFactoriesLoader {
|
|||
/**
|
||||
* Load the factory implementations of the given type from the default location,
|
||||
* using the given class loader.
|
||||
* <p>The returned factories are ordered in accordance with the {@link OrderComparator}.
|
||||
* <p>The returned factories are ordered in accordance with the {@link AnnotationAwareOrderComparator}.
|
||||
* @param factoryClass the interface or abstract class representing the factory
|
||||
* @param classLoader the ClassLoader to use for loading (can be {@code null} to use the default)
|
||||
*/
|
||||
|
@ -79,7 +80,7 @@ public abstract class SpringFactoriesLoader {
|
|||
for (String factoryName : factoryNames) {
|
||||
result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
|
||||
}
|
||||
OrderComparator.sort(result);
|
||||
AnnotationAwareOrderComparator.sort(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -79,6 +79,7 @@ public class MergedContextConfiguration implements Serializable {
|
|||
private final String[] propertySourceProperties;
|
||||
private final ContextLoader contextLoader;
|
||||
private final CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate;
|
||||
|
||||
private final MergedContextConfiguration parent;
|
||||
|
||||
|
||||
|
@ -185,6 +186,18 @@ public class MergedContextConfiguration implements Serializable {
|
|||
cacheAwareContextLoaderDelegate, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code MergedContextConfiguration} instance by copying
|
||||
* all fields from the supplied {@code MergedContextConfiguration}.
|
||||
* @since 4.1
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code MergedContextConfiguration} instance for the
|
||||
* supplied parameters.
|
||||
|
|
|
@ -63,8 +63,16 @@ public interface TestContextBootstrapper {
|
|||
* 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.
|
||||
* <em>default</em> listeners should be returned. Furthermore, default
|
||||
* listeners must be sorted using
|
||||
* {@link org.springframework.core.annotation.AnnotationAwareOrderComparator
|
||||
* AnnotationAwareOrderComparator}.
|
||||
* <p>Concrete implementations are free to determine what comprises the
|
||||
* set of default listeners. However, by default, the Spring TestContext
|
||||
* Framework will use the
|
||||
* {@link org.springframework.core.io.support.SpringFactoriesLoader SpringFactoriesLoader}
|
||||
* mechanism to look up all {@code TestExecutionListener} class names
|
||||
* configured in all {@code META-INF/spring.factories} files on the classpath.
|
||||
* <p>The {@link TestExecutionListeners#inheritListeners() inheritListeners}
|
||||
* flag of {@link TestExecutionListeners @TestExecutionListeners} must be
|
||||
* taken into consideration. Specifically, if the {@code inheritListeners}
|
||||
|
|
|
@ -23,7 +23,13 @@ package org.springframework.test.context;
|
|||
* <p>Concrete implementations must provide a {@code public} no-args constructor,
|
||||
* so that listeners can be instantiated transparently by tools and configuration
|
||||
* mechanisms.
|
||||
* <p>Spring provides the following out-of-the-box implementations:
|
||||
* <p>Implementations may optionally declare the position in which they should
|
||||
* be ordered among the chain of default listeners via the
|
||||
* {@link org.springframework.core.Ordered Order} interface or
|
||||
* {@link org.springframework.core.annotation.Order @Order} annotation. See
|
||||
* {@link TestContextBootstrapper#getTestExecutionListeners()} for details.
|
||||
* <p>Spring provides the following out-of-the-box implementations (all of
|
||||
* which are annotated with {@code @Order}):
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.test.context.web.ServletTestExecutionListener
|
||||
* ServletTestExecutionListener}</li>
|
||||
|
|
|
@ -89,6 +89,14 @@ public class SqlScriptsTestExecutionListener extends AbstractTestExecutionListen
|
|||
private static final Log logger = LogFactory.getLog(SqlScriptsTestExecutionListener.class);
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code 5000}.
|
||||
*/
|
||||
@Override
|
||||
public final int getOrder() {
|
||||
return 5000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute SQL scripts configured via {@link Sql @Sql} for the supplied
|
||||
* {@link TestContext} <em>before</em> the current test method.
|
||||
|
|
|
@ -31,7 +31,9 @@ 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.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.test.context.BootstrapContext;
|
||||
import org.springframework.test.context.CacheAwareContextLoaderDelegate;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
@ -55,11 +57,10 @@ import org.springframework.util.StringUtils;
|
|||
* 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:
|
||||
* the following methods:
|
||||
* <ul>
|
||||
* <li>{@link #getDefaultTestExecutionListenerClassNames}
|
||||
* <li>{@link #getDefaultContextLoaderClass}
|
||||
* <li>{@link #buildMergedContextConfiguration}
|
||||
* <li>{@link #processMergedContextConfiguration}
|
||||
* </ul>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
|
@ -98,6 +99,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||
Class<?> clazz = getBootstrapContext().getTestClass();
|
||||
Class<TestExecutionListeners> annotationType = TestExecutionListeners.class;
|
||||
List<Class<? extends TestExecutionListener>> classesList = new ArrayList<Class<? extends TestExecutionListener>>();
|
||||
boolean usingDefaults = false;
|
||||
|
||||
AnnotationDescriptor<TestExecutionListeners> descriptor = MetaAnnotationUtils.findAnnotationDescriptor(clazz,
|
||||
annotationType);
|
||||
|
@ -105,9 +107,10 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||
// Use defaults?
|
||||
if (descriptor == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("@TestExecutionListeners is not present for class [" + clazz.getName()
|
||||
+ "]: using defaults.");
|
||||
logger.debug(String.format("@TestExecutionListeners is not present for class [%s]: using defaults.",
|
||||
clazz.getName()));
|
||||
}
|
||||
usingDefaults = true;
|
||||
classesList.addAll(getDefaultTestExecutionListenerClasses());
|
||||
}
|
||||
else {
|
||||
|
@ -142,6 +145,20 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||
}
|
||||
}
|
||||
|
||||
List<TestExecutionListener> listeners = instantiateListeners(classesList);
|
||||
|
||||
// Sort by Ordered/@Order if we loaded default listeners.
|
||||
if (usingDefaults) {
|
||||
AnnotationAwareOrderComparator.sort(listeners);
|
||||
}
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("Using TestExecutionListeners: %s", listeners));
|
||||
}
|
||||
return listeners;
|
||||
}
|
||||
|
||||
private List<TestExecutionListener> instantiateListeners(List<Class<? extends TestExecutionListener>> classesList) {
|
||||
List<TestExecutionListener> listeners = new ArrayList<TestExecutionListener>(classesList.size());
|
||||
for (Class<? extends TestExecutionListener> listenerClass : classesList) {
|
||||
NoClassDefFoundError ncdfe = null;
|
||||
|
@ -194,6 +211,28 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||
return defaultListenerClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the names of the default {@link TestExecutionListener} classes for
|
||||
* this bootstrapper.
|
||||
* <p>The default implementation looks up all
|
||||
* {@code org.springframework.test.context.TestExecutionListener} entries
|
||||
* configured in all {@code META-INF/spring.factories} files on the classpath.
|
||||
* <p>This method is invoked by {@link #getDefaultTestExecutionListenerClasses()}.
|
||||
* @return an <em>unmodifiable</em> list of names of default {@code TestExecutionListener}
|
||||
* classes
|
||||
* @see SpringFactoriesLoader#loadFactoryNames
|
||||
*/
|
||||
protected List<String> getDefaultTestExecutionListenerClassNames() {
|
||||
final List<String> classNames = SpringFactoriesLoader.loadFactoryNames(TestExecutionListener.class,
|
||||
getClass().getClassLoader());
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("Loaded default TestExecutionListener class names from location [%s]: %s",
|
||||
SpringFactoriesLoader.FACTORIES_RESOURCE_LOCATION, classNames));
|
||||
}
|
||||
return Collections.unmodifiableList(classNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -302,9 +341,11 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||
String[] activeProfiles = ActiveProfilesUtils.resolveActiveProfiles(testClass);
|
||||
MergedTestPropertySources mergedTestPropertySources = TestPropertySourceUtils.buildMergedTestPropertySources(testClass);
|
||||
|
||||
return buildMergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
|
||||
mergedTestPropertySources.getLocations(), mergedTestPropertySources.getProperties(), contextLoader,
|
||||
cacheAwareContextLoaderDelegate, parentConfig);
|
||||
MergedContextConfiguration mergedConfig = new MergedContextConfiguration(testClass, locations, classes,
|
||||
initializerClasses, activeProfiles, mergedTestPropertySources.getLocations(),
|
||||
mergedTestPropertySources.getProperties(), contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
|
||||
|
||||
return processMergedContextConfiguration(mergedConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -383,15 +424,6 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||
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.
|
||||
|
@ -403,34 +435,19 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||
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 propertySourceLocations the merged {@code PropertySource} locations
|
||||
* @param propertySourceProperties the merged {@code PropertySource} properties
|
||||
* @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}
|
||||
* Process the supplied, newly instantiated {@link MergedContextConfiguration} instance.
|
||||
* <p>The returned {@link MergedContextConfiguration} instance may be a wrapper
|
||||
* around or a replacement for the original.
|
||||
* <p>The default implementation simply returns the supplied instance unmodified.
|
||||
* <p>Concrete subclasses may choose to return a specialized subclass of
|
||||
* {@link MergedContextConfiguration} based on properties in the supplied instance.
|
||||
* @param mergedConfig the {@code MergedContextConfiguration} to process;
|
||||
* never {@code null}
|
||||
* @return a fully initialized {@code MergedContextConfiguration}; never
|
||||
* {@code null}
|
||||
*/
|
||||
protected abstract MergedContextConfiguration buildMergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
String[] locations,
|
||||
Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses,
|
||||
String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
|
||||
ContextLoader contextLoader, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate,
|
||||
MergedContextConfiguration parentConfig);
|
||||
protected MergedContextConfiguration processMergedContextConfiguration(MergedContextConfiguration mergedConfig) {
|
||||
return mergedConfig;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.TestExecutionListener;
|
||||
|
||||
|
@ -28,7 +29,7 @@ import org.springframework.test.context.TestExecutionListener;
|
|||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
public abstract class AbstractTestExecutionListener implements TestExecutionListener {
|
||||
public abstract class AbstractTestExecutionListener implements TestExecutionListener, Ordered {
|
||||
|
||||
/**
|
||||
* The default implementation is <em>empty</em>. Can be overridden by
|
||||
|
@ -75,4 +76,16 @@ public abstract class AbstractTestExecutionListener implements TestExecutionList
|
|||
/* no-op */
|
||||
}
|
||||
|
||||
/**
|
||||
* The default implementation returns {@link Ordered#LOWEST_PRECEDENCE},
|
||||
* thereby ensuring that custom listeners are ordered after default
|
||||
* listeners supplied by the framework. Can be overridden by subclasses
|
||||
* as necessary.
|
||||
* @since 4.1
|
||||
*/
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,60 +16,19 @@
|
|||
|
||||
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}
|
||||
* <li>{@link org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener}
|
||||
* </ol>
|
||||
* <li>Uses {@link DelegatingSmartContextLoader} as the default {@link ContextLoader}.
|
||||
* <li>Builds a standard {@link MergedContextConfiguration}.
|
||||
* </ul>
|
||||
* <p>Uses {@link DelegatingSmartContextLoader} as the default {@link ContextLoader}.
|
||||
*
|
||||
* @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",
|
||||
"org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener"));
|
||||
|
||||
|
||||
/**
|
||||
* 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}
|
||||
* <li>{@link org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener}
|
||||
* </ol>
|
||||
*/
|
||||
protected List<String> getDefaultTestExecutionListenerClassNames() {
|
||||
return DEFAULT_TEST_EXECUTION_LISTENER_CLASS_NAMES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link DelegatingSmartContextLoader}.
|
||||
*/
|
||||
|
@ -78,22 +37,4 @@ public class DefaultTestContextBootstrapper extends AbstractTestContextBootstrap
|
|||
return DelegatingSmartContextLoader.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a standard {@link MergedContextConfiguration}.
|
||||
*/
|
||||
@Override
|
||||
protected MergedContextConfiguration buildMergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
String[] locations,
|
||||
Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses,
|
||||
String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
|
||||
ContextLoader contextLoader, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate,
|
||||
MergedContextConfiguration parentConfig) {
|
||||
|
||||
return new MergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
|
||||
propertySourceLocations, propertySourceProperties, 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.
|
||||
|
@ -18,7 +18,6 @@ package org.springframework.test.context.support;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.test.context.TestContext;
|
||||
|
@ -49,11 +48,19 @@ public class DependencyInjectionTestExecutionListener extends AbstractTestExecut
|
|||
* <p>Permissible values include {@link Boolean#TRUE} and {@link Boolean#FALSE}.
|
||||
*/
|
||||
public static final String REINJECT_DEPENDENCIES_ATTRIBUTE = Conventions.getQualifiedAttributeName(
|
||||
DependencyInjectionTestExecutionListener.class, "reinjectDependencies");
|
||||
DependencyInjectionTestExecutionListener.class, "reinjectDependencies");
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DependencyInjectionTestExecutionListener.class);
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code 2000}.
|
||||
*/
|
||||
@Override
|
||||
public final int getOrder() {
|
||||
return 2000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs dependency injection on the
|
||||
* {@link TestContext#getTestInstance() test instance} of the supplied
|
||||
|
|
|
@ -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.
|
||||
|
@ -48,20 +48,11 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi
|
|||
|
||||
|
||||
/**
|
||||
* Marks the {@linkplain ApplicationContext application context} of the supplied
|
||||
* {@linkplain TestContext test context} as
|
||||
* {@linkplain TestContext#markApplicationContextDirty(DirtiesContext.HierarchyMode) dirty}
|
||||
* and sets {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE}
|
||||
* in the test context to {@code true}.
|
||||
* @param testContext the test context whose application context should
|
||||
* marked as dirty
|
||||
* @param hierarchyMode the context cache clearing mode to be applied if the
|
||||
* context is part of a hierarchy; may be {@code null}
|
||||
* @since 3.2.2
|
||||
* Returns {@code 3000}.
|
||||
*/
|
||||
protected void dirtyContext(TestContext testContext, HierarchyMode hierarchyMode) {
|
||||
testContext.markApplicationContextDirty(hierarchyMode);
|
||||
testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE, Boolean.TRUE);
|
||||
@Override
|
||||
public final int getOrder() {
|
||||
return 3000;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,4 +123,21 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the {@linkplain ApplicationContext application context} of the supplied
|
||||
* {@linkplain TestContext test context} as
|
||||
* {@linkplain TestContext#markApplicationContextDirty(DirtiesContext.HierarchyMode) dirty}
|
||||
* and sets {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE}
|
||||
* in the test context to {@code true}.
|
||||
* @param testContext the test context whose application context should
|
||||
* marked as dirty
|
||||
* @param hierarchyMode the context cache clearing mode to be applied if the
|
||||
* context is part of a hierarchy; may be {@code null}
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected void dirtyContext(TestContext testContext, HierarchyMode hierarchyMode) {
|
||||
testContext.markApplicationContextDirty(hierarchyMode);
|
||||
testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE, Boolean.TRUE);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -144,6 +144,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
|
|||
private TransactionConfigurationAttributes configurationAttributes;
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code 4000}.
|
||||
*/
|
||||
@Override
|
||||
public final int getOrder() {
|
||||
return 4000;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the test method of the supplied {@linkplain TestContext test context}
|
||||
* is configured to run within a transaction, this method will run
|
||||
|
|
|
@ -87,6 +87,14 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener
|
|||
private static final Log logger = LogFactory.getLog(ServletTestExecutionListener.class);
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code 1000}.
|
||||
*/
|
||||
@Override
|
||||
public final int getOrder() {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up thread-local state during the <em>test instance preparation</em>
|
||||
* callback phase via Spring Web's {@link RequestContextHolder}, but only if
|
||||
|
|
|
@ -84,8 +84,7 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
|||
String[] activeProfiles, String resourceBasePath, ContextLoader contextLoader) {
|
||||
|
||||
this(testClass, locations, classes, contextInitializerClasses, activeProfiles, null, null, resourceBasePath,
|
||||
contextLoader,
|
||||
null, null);
|
||||
contextLoader, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -122,6 +121,19 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
|||
contextLoader, cacheAwareContextLoaderDelegate, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code WebMergedContextConfiguration} instance by copying
|
||||
* all properties from the supplied {@code MergedContextConfiguration}.
|
||||
* <p>If an <em>empty</em> value is supplied for the {@code resourceBasePath}
|
||||
* an empty string will be used.
|
||||
* @param resourceBasePath the resource path to the root directory of the web application
|
||||
* @since 4.1
|
||||
*/
|
||||
public WebMergedContextConfiguration(MergedContextConfiguration mergedConfig, String resourceBasePath) {
|
||||
super(mergedConfig);
|
||||
this.resourceBasePath = !StringUtils.hasText(resourceBasePath) ? "" : resourceBasePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code WebMergedContextConfiguration} instance for the
|
||||
* supplied parameters.
|
||||
|
|
|
@ -16,34 +16,21 @@
|
|||
|
||||
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.
|
||||
* with {@link WebAppConfiguration @WebAppConfiguration}.
|
||||
* </ul>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
|
@ -51,18 +38,6 @@ import org.springframework.test.context.support.DefaultTestContextBootstrapper;
|
|||
*/
|
||||
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
|
||||
|
@ -79,33 +54,21 @@ public class WebTestContextBootstrapper extends DefaultTestContextBootstrapper {
|
|||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link WebMergedContextConfiguration} if the supplied class is
|
||||
* annotated with {@link WebAppConfiguration @WebAppConfiguration} and
|
||||
* otherwise delegates to the superclass.
|
||||
* Returns a {@link WebMergedContextConfiguration} if the test class in the
|
||||
* supplied {@code MergedContextConfiguration} is annotated with
|
||||
* {@link WebAppConfiguration @WebAppConfiguration} and otherwise returns
|
||||
* the supplied instance unmodified.
|
||||
*/
|
||||
@Override
|
||||
protected MergedContextConfiguration buildMergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
String[] locations,
|
||||
Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses,
|
||||
String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
|
||||
ContextLoader contextLoader, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate,
|
||||
MergedContextConfiguration parentConfig) {
|
||||
|
||||
WebAppConfiguration webAppConfiguration = AnnotationUtils.findAnnotation(testClass, WebAppConfiguration.class);
|
||||
protected MergedContextConfiguration processMergedContextConfiguration(MergedContextConfiguration mergedConfig) {
|
||||
WebAppConfiguration webAppConfiguration = AnnotationUtils.findAnnotation(mergedConfig.getTestClass(),
|
||||
WebAppConfiguration.class);
|
||||
if (webAppConfiguration != null) {
|
||||
String resourceBasePath = webAppConfiguration.value();
|
||||
|
||||
return new WebMergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
|
||||
propertySourceLocations, propertySourceProperties, resourceBasePath, contextLoader,
|
||||
cacheAwareContextLoaderDelegate, parentConfig);
|
||||
return new WebMergedContextConfiguration(mergedConfig, webAppConfiguration.value());
|
||||
}
|
||||
|
||||
// else...
|
||||
return super.buildMergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
|
||||
propertySourceLocations, propertySourceProperties, contextLoader, cacheAwareContextLoaderDelegate,
|
||||
parentConfig);
|
||||
return mergedConfig;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
# Default TestExecutionListeners for the Spring TestContext Framework
|
||||
#
|
||||
org.springframework.test.context.TestExecutionListener = \
|
||||
org.springframework.test.context.web.ServletTestExecutionListener,\
|
||||
org.springframework.test.context.support.DependencyInjectionTestExecutionListener,\
|
||||
org.springframework.test.context.support.DirtiesContextTestExecutionListener,\
|
||||
org.springframework.test.context.transaction.TransactionalTestExecutionListener,\
|
||||
org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener
|
|
@ -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.", 5,
|
||||
testContextManager.getTestExecutionListeners().size());
|
||||
}
|
||||
|
||||
|
|
|
@ -16,12 +16,19 @@
|
|||
|
||||
package org.springframework.test.context.jdbc;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.FixMethodOrder;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.MethodSorters;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.jdbc.JdbcTestUtils;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
@ -31,11 +38,21 @@ import static org.junit.Assert.*;
|
|||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
@ContextConfiguration(classes = EmptyDatabaseConfig.class)
|
||||
@Transactional
|
||||
@Sql({ "schema.sql", "data.sql" })
|
||||
@DirtiesContext
|
||||
public class TransactionalSqlScriptsTests extends AbstractTransactionalJUnit4SpringContextTests {
|
||||
public class TransactionalSqlScriptsTests {
|
||||
|
||||
protected JdbcTemplate jdbcTemplate;
|
||||
|
||||
|
||||
@Autowired
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
// test##_ prefix is required for @FixMethodOrder.
|
||||
|
@ -50,6 +67,10 @@ public class TransactionalSqlScriptsTests extends AbstractTransactionalJUnit4Spr
|
|||
assertNumUsers(2);
|
||||
}
|
||||
|
||||
protected int countRowsInTable(String tableName) {
|
||||
return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);
|
||||
}
|
||||
|
||||
protected void assertNumUsers(int expected) {
|
||||
assertEquals("Number of rows in the 'user' table.", expected, countRowsInTable("user"));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
# Test configuration file containing a non-existent default TestExecutionListener.
|
||||
org.springframework.test.context.TestExecutionListener = org.example.FooListener
|
Loading…
Reference in New Issue