Browse Source

[SPR-5710] Subclasses of SpringJUnit4ClassRunner can now override the default ContextLoader class via the new getDefaultContextLoaderClassName(Class) method.

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@1086 50f2f4bb-b051-0410-bef5-90022cba6387
pull/1/head
Sam Brannen 17 years ago
parent
commit
075c28f8e1
  1. 172
      org.springframework.test/src/main/java/org/springframework/test/context/TestContext.java
  2. 182
      org.springframework.test/src/main/java/org/springframework/test/context/TestContextManager.java
  3. 31
      org.springframework.test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java
  4. 71
      org.springframework.test/src/test/java/org/springframework/test/context/junit4/CustomDefaultContextLoaderClassSpringRunnerTests.java
  5. 1
      org.springframework.test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4SuiteTests.java

172
org.springframework.test/src/main/java/org/springframework/test/context/TestContext.java

@ -24,7 +24,6 @@ import java.util.List; @@ -24,7 +24,6 @@ import java.util.List;
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.AttributeAccessorSupport;
@ -32,11 +31,12 @@ import org.springframework.core.annotation.AnnotationUtils; @@ -32,11 +31,12 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* TestContext encapsulates the context in which a test is executed,
* agnostic of the actual testing framework in use.
*
* TestContext encapsulates the context in which a test is executed, agnostic of
* the actual testing framework in use.
*
* @author Sam Brannen
* @author Juergen Hoeller
* @since 2.5
@ -45,12 +45,10 @@ public class TestContext extends AttributeAccessorSupport { @@ -45,12 +45,10 @@ public class TestContext extends AttributeAccessorSupport {
private static final long serialVersionUID = -5827157174866681233L;
private static final String DEFAULT_CONTEXT_LOADER_CLASS_NAME =
"org.springframework.test.context.support.GenericXmlContextLoader";
private static final String STANDARD_DEFAULT_CONTEXT_LOADER_CLASS_NAME = "org.springframework.test.context.support.GenericXmlContextLoader";
private static final Log logger = LogFactory.getLog(TestContext.class);
private final ContextCache contextCache;
private final ContextLoader contextLoader;
@ -66,20 +64,46 @@ public class TestContext extends AttributeAccessorSupport { @@ -66,20 +64,46 @@ public class TestContext extends AttributeAccessorSupport {
private Throwable testException;
/**
* Delegates to {@link #TestContext(Class, ContextCache, String)} with a
* value of <code>null</code> for the default <code>ContextLoader</code>
* class name.
*/
TestContext(Class<?> testClass, ContextCache contextCache) {
this(testClass, contextCache, null);
}
/**
* Construct a new test context for the supplied {@link Class test class}
* and {@link ContextCache context cache} and parses the corresponding
* {@link ContextConfiguration @ContextConfiguration} annotation, if present.
* @param testClass the {@link Class} object corresponding to the test class
* for which the test context should be constructed (must not be <code>null</code>)
* @param contextCache the context cache from which the constructed test context
* should retrieve application contexts (must not be <code>null</code>)
* and {@link ContextCache context cache} and parse the corresponding
* {@link ContextConfiguration &#064;ContextConfiguration} annotation, if
* present.
* <p>
* If the supplied class name for the default ContextLoader is
* <code>null</code> or <em>empty</em> and no <code>ContextLoader</code>
* class is explicitly supplied via the
* <code>&#064;ContextConfiguration</code> annotation, a
* {@link org.springframework.test.context.support.GenericXmlContextLoader
* GenericXmlContextLoader} will be used instead.
* </p>
*
* @param testClass the test class for which the test context should be
* constructed (must not be <code>null</code>)
* @param contextCache the context cache from which the constructed test
* context should retrieve application contexts (must not be
* <code>null</code>)
* @param defaultContextLoaderClassName the name of the default
* <code>ContextLoader</code> class to use (may be <code>null</code>)
*/
@SuppressWarnings("unchecked")
TestContext(Class<?> testClass, ContextCache contextCache) {
TestContext(Class<?> testClass, ContextCache contextCache, String defaultContextLoaderClassName) {
Assert.notNull(testClass, "Test class must not be null");
Assert.notNull(contextCache, "ContextCache must not be null");
if (!StringUtils.hasText(defaultContextLoaderClassName)) {
defaultContextLoaderClassName = STANDARD_DEFAULT_CONTEXT_LOADER_CLASS_NAME;
}
ContextConfiguration contextConfiguration = testClass.getAnnotation(ContextConfiguration.class);
String[] locations = null;
ContextLoader contextLoader = null;
@ -91,18 +115,24 @@ public class TestContext extends AttributeAccessorSupport { @@ -91,18 +115,24 @@ public class TestContext extends AttributeAccessorSupport {
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Retrieved @ContextConfiguration [" + contextConfiguration + "] for class [" + testClass + "]");
logger.trace("Retrieved @ContextConfiguration [" + contextConfiguration + "] for class [" + testClass
+ "]");
}
Class<? extends ContextLoader> contextLoaderClass = contextConfiguration.loader();
if (ContextLoader.class.equals(contextLoaderClass)) {
try {
if (logger.isTraceEnabled()) {
logger.trace("Using default ContextLoader class [" + defaultContextLoaderClassName
+ "] for @ContextConfiguration [" + contextConfiguration + "] and class [" + testClass
+ "]");
}
contextLoaderClass = (Class<? extends ContextLoader>) getClass().getClassLoader().loadClass(
DEFAULT_CONTEXT_LOADER_CLASS_NAME);
defaultContextLoaderClassName);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException("Could not load default ContextLoader class ["
+ DEFAULT_CONTEXT_LOADER_CLASS_NAME + "]. Specify @ContextConfiguration's 'loader' "
+ defaultContextLoaderClassName + "]. Specify @ContextConfiguration's 'loader' "
+ "attribute or make the default loader class available.");
}
}
@ -119,21 +149,24 @@ public class TestContext extends AttributeAccessorSupport { @@ -119,21 +149,24 @@ public class TestContext extends AttributeAccessorSupport {
/**
* Retrieve {@link ApplicationContext} resource locations for the supplied
* {@link Class class}, using the supplied {@link ContextLoader} to
* {@link ContextLoader#processLocations(Class, String...) process} the locations.
* <p>Note that the
* {@link ContextConfiguration#inheritLocations() inheritLocations} flag of
* {@link ContextConfiguration @ContextConfiguration} will be taken into
* consideration. Specifically, if the <code>inheritLocations</code> flag
* is set to <code>true</code>, locations defined in the annotated class
* will be appended to the locations defined in superclasses.
* @param contextLoader the ContextLoader to use for processing the locations
* (must not be <code>null</code>)
* @param clazz the class for which to retrieve the resource locations
* (must not be <code>null</code>)
* @return the list of ApplicationContext resource locations for the specified
* class, including locations from superclasses if appropriate
* @throws IllegalArgumentException if {@link ContextConfiguration @ContextConfiguration}
* is not <em>present</em> on the supplied class
* {@link ContextLoader#processLocations(Class, String...) process} the
* locations.
* <p>
* Note that the {@link ContextConfiguration#inheritLocations()
* inheritLocations} flag of {@link ContextConfiguration
* &#064;ContextConfiguration} will be taken into consideration.
* Specifically, if the <code>inheritLocations</code> flag is set to
* <code>true</code>, locations defined in the annotated class will be
* appended to the locations defined in superclasses. &#064;param
* contextLoader the ContextLoader to use for processing the locations (must
* not be <code>null</code>)
*
* @param clazz the class for which to retrieve the resource locations (must
* not be <code>null</code>)
* @return the list of ApplicationContext resource locations for the
* specified class, including locations from superclasses if appropriate
* @throws IllegalArgumentException if {@link ContextConfiguration
* &#064;ContextConfiguration} is not <em>present</em> on the supplied class
*/
private String[] retrieveContextLocations(ContextLoader contextLoader, Class<?> clazz) {
Assert.notNull(contextLoader, "ContextLoader must not be null");
@ -142,8 +175,8 @@ public class TestContext extends AttributeAccessorSupport { @@ -142,8 +175,8 @@ public class TestContext extends AttributeAccessorSupport {
List<String> locationsList = new ArrayList<String>();
Class<ContextConfiguration> annotationType = ContextConfiguration.class;
Class<?> declaringClass = AnnotationUtils.findAnnotationDeclaringClass(annotationType, clazz);
Assert.notNull(declaringClass, "Could not find an 'annotation declaring class' for annotation type [" +
annotationType + "] and class [" + clazz + "]");
Assert.notNull(declaringClass, "Could not find an 'annotation declaring class' for annotation type ["
+ annotationType + "] and class [" + clazz + "]");
while (declaringClass != null) {
ContextConfiguration contextConfiguration = declaringClass.getAnnotation(annotationType);
@ -154,28 +187,30 @@ public class TestContext extends AttributeAccessorSupport { @@ -154,28 +187,30 @@ public class TestContext extends AttributeAccessorSupport {
String[] locations = contextLoader.processLocations(declaringClass, contextConfiguration.locations());
locationsList.addAll(0, Arrays.<String> asList(locations));
declaringClass = contextConfiguration.inheritLocations() ? AnnotationUtils.findAnnotationDeclaringClass(
annotationType, declaringClass.getSuperclass()) : null;
annotationType, declaringClass.getSuperclass()) : null;
}
return locationsList.toArray(new String[locationsList.size()]);
}
/**
* Build an ApplicationContext for this test context using the
* configured ContextLoader and resource locations.
* @throws Exception if an error occurs while building the application context
* Build an ApplicationContext for this test context using the configured
* ContextLoader and resource locations.
*
* @throws Exception if an error occurs while building the application
* context
*/
private ApplicationContext loadApplicationContext() throws Exception {
Assert.notNull(this.contextLoader, "Can not build an ApplicationContext with a NULL 'contextLoader'. " +
"Consider annotating your test class with @ContextConfiguration.");
Assert.notNull(this.locations, "Can not build an ApplicationContext with a NULL 'locations' array. " +
"Consider annotating your test class with @ContextConfiguration.");
Assert.notNull(this.contextLoader, "Can not build an ApplicationContext with a NULL 'contextLoader'. "
+ "Consider annotating your test class with @ContextConfiguration.");
Assert.notNull(this.locations, "Can not build an ApplicationContext with a NULL 'locations' array. "
+ "Consider annotating your test class with @ContextConfiguration.");
return this.contextLoader.loadContext(this.locations);
}
/**
* Convert the supplied context <code>key</code> to a String
* representation for use in caching, logging, etc.
* Convert the supplied context <code>key</code> to a String representation
* for use in caching, logging, etc.
*/
private String contextKeyString(Serializable key) {
return ObjectUtils.nullSafeToString(key);
@ -184,9 +219,11 @@ public class TestContext extends AttributeAccessorSupport { @@ -184,9 +219,11 @@ public class TestContext extends AttributeAccessorSupport {
/**
* Get the {@link ApplicationContext application context} for this test
* context, possibly cached.
* @return the application context; may be <code>null</code> if the
* current test context is not configured to use an application context
* @throws IllegalStateException if an error occurs while retrieving the application context
*
* @return the application context; may be <code>null</code> if the current
* test context is not configured to use an application context
* @throws IllegalStateException if an error occurs while retrieving the
* application context
*/
public ApplicationContext getApplicationContext() {
synchronized (this.contextCache) {
@ -206,6 +243,7 @@ public class TestContext extends AttributeAccessorSupport { @@ -206,6 +243,7 @@ public class TestContext extends AttributeAccessorSupport {
/**
* Get the {@link Class test class} for this test context.
*
* @return the test class (never <code>null</code>)
*/
public final Class<?> getTestClass() {
@ -214,7 +252,9 @@ public class TestContext extends AttributeAccessorSupport { @@ -214,7 +252,9 @@ public class TestContext extends AttributeAccessorSupport {
/**
* Get the current {@link Object test instance} for this test context.
* <p>Note: this is a mutable property.
* <p>
* Note: this is a mutable property.
*
* @return the current test instance (may be <code>null</code>)
* @see #updateState(Object,Method,Throwable)
*/
@ -224,7 +264,9 @@ public class TestContext extends AttributeAccessorSupport { @@ -224,7 +264,9 @@ public class TestContext extends AttributeAccessorSupport {
/**
* Get the current {@link Method test method} for this test context.
* <p>Note: this is a mutable property.
* <p>
* Note: this is a mutable property.
*
* @return the current test method (may be <code>null</code>)
* @see #updateState(Object, Method, Throwable)
*/
@ -235,7 +277,9 @@ public class TestContext extends AttributeAccessorSupport { @@ -235,7 +277,9 @@ public class TestContext extends AttributeAccessorSupport {
/**
* Get the {@link Throwable exception} that was thrown during execution of
* the {@link #getTestMethod() test method}.
* <p>Note: this is a mutable property.
* <p>
* Note: this is a mutable property.
*
* @return the exception that was thrown, or <code>null</code> if no
* exception was thrown
* @see #updateState(Object, Method, Throwable)
@ -245,21 +289,23 @@ public class TestContext extends AttributeAccessorSupport { @@ -245,21 +289,23 @@ public class TestContext extends AttributeAccessorSupport {
}
/**
* Call this method to signal that the
* {@link ApplicationContext application context} associated with this test
* context is <em>dirty</em> and should be reloaded. Do this if a test has
* modified the context (for example, by replacing a bean definition).
* Call this method to signal that the {@link ApplicationContext application
* context} associated with this test context is <em>dirty</em> and should
* be reloaded. Do this if a test has modified the context (for example, by
* replacing a bean definition).
*/
public void markApplicationContextDirty() {
this.contextCache.setDirty(contextKeyString(this.locations));
}
/**
* Update this test context to reflect the state of the currently executing test.
* Update this test context to reflect the state of the currently executing
* test.
*
* @param testInstance the current test instance (may be <code>null</code>)
* @param testMethod the current test method (may be <code>null</code>)
* @param testException the exception that was thrown in the test method,
* or <code>null</code> if no exception was thrown
* @param testException the exception that was thrown in the test method, or
* <code>null</code> if no exception was thrown
*/
void updateState(Object testInstance, Method testMethod, Throwable testException) {
this.testInstance = testInstance;
@ -272,11 +318,13 @@ public class TestContext extends AttributeAccessorSupport { @@ -272,11 +318,13 @@ public class TestContext extends AttributeAccessorSupport {
*/
@Override
public String toString() {
return new ToStringCreator(this).
append("testClass", this.testClass).
append("locations", this.locations).append("testInstance", this.testInstance).
append("testMethod", this.testMethod).append("testException", this.testException).
toString();
return new ToStringCreator(this)//
.append("testClass", this.testClass)//
.append("locations", this.locations)//
.append("testInstance", this.testInstance)//
.append("testMethod", this.testMethod)//
.append("testException", this.testException)//
.toString();
}
}

182
org.springframework.test/src/main/java/org/springframework/test/context/TestContextManager.java

@ -26,7 +26,6 @@ import java.util.Set; @@ -26,7 +26,6 @@ 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.AnnotationUtils;
@ -35,15 +34,15 @@ import org.springframework.util.Assert; @@ -35,15 +34,15 @@ import org.springframework.util.Assert;
/**
* <p>
* <code>TestContextManager</code> is the main entry point into the
* <em>Spring TestContext Framework</em>, which provides support for loading
* and accessing {@link ApplicationContext application contexts}, dependency
* <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.
* {@link org.springframework.transaction.annotation.Transactional
* transactional} execution of test methods, etc.
* </p>
* <p>
* Specifically, a <code>TestContextManager</code> is responsible for managing
* a single {@link TestContext} and signaling events to all registered
* Specifically, a <code>TestContextManager</code> is responsible for managing a
* single {@link TestContext} and signaling events to all registered
* {@link TestExecutionListener TestExecutionListeners} at well defined test
* execution points:
* </p>
@ -51,13 +50,13 @@ import org.springframework.util.Assert; @@ -51,13 +50,13 @@ import org.springframework.util.Assert;
* <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>
* <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>
* prior to any <em>before methods</em> of a particular testing framework (e.g.,
* JUnit 4's {@link org.junit.Before &#064;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 &#064;After})</li>
* </ul>
*
*
* @author Sam Brannen
* @author Juergen Hoeller
* @since 2.5
@ -69,9 +68,9 @@ import org.springframework.util.Assert; @@ -69,9 +68,9 @@ import org.springframework.util.Assert;
public class TestContextManager {
private static final String[] DEFAULT_TEST_EXECUTION_LISTENER_CLASS_NAMES = new String[] {
"org.springframework.test.context.support.DependencyInjectionTestExecutionListener",
"org.springframework.test.context.support.DirtiesContextTestExecutionListener",
"org.springframework.test.context.transaction.TransactionalTestExecutionListener" };
"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);
@ -82,41 +81,50 @@ public class TestContextManager { @@ -82,41 +81,50 @@ public class TestContextManager {
*/
static final ContextCache contextCache = new ContextCache();
private final TestContext testContext;
private final List<TestExecutionListener> testExecutionListeners = new ArrayList<TestExecutionListener>();
/**
* Delegates to {@link #TestContextManager(Class, String)} with a value of
* <code>null</code> for the default <code>ContextLoader</code> class name.
*/
public TestContextManager(Class<?> testClass) {
this(testClass, null);
}
/**
* Constructs a new <code>TestContextManager</code> for the specified
* {@link Class test class} and automatically
* {@link #registerTestExecutionListeners(TestExecutionListener...) registers}
* the {@link TestExecutionListener TestExecutionListeners} configured for
* the test class via the
* {@link TestExecutionListeners @TestExecutionListeners} annotation.
* @param testClass the Class object corresponding to the test class to be managed
* {@link #registerTestExecutionListeners(TestExecutionListener...)
* registers} the {@link TestExecutionListener TestExecutionListeners}
* configured for the test class via the {@link TestExecutionListeners
* &#064;TestExecutionListeners} annotation.
*
* @param testClass the test class to be managed
* @param defaultContextLoaderClassName the name of the default
* <code>ContextLoader</code> class to use (may be <code>null</code>)
* @see #registerTestExecutionListeners(TestExecutionListener...)
* @see #retrieveTestExecutionListeners(Class)
*/
public TestContextManager(Class<?> testClass) {
this.testContext = new TestContext(testClass, contextCache);
public TestContextManager(Class<?> testClass, String defaultContextLoaderClassName) {
this.testContext = new TestContext(testClass, contextCache, defaultContextLoaderClassName);
registerTestExecutionListeners(retrieveTestExecutionListeners(testClass));
}
/**
* Returns the {@link TestContext} managed by this <code>TestContextManager</code>.
* Returns the {@link TestContext} managed by this
* <code>TestContextManager</code>.
*/
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</code>.
* Register the supplied {@link TestExecutionListener
* TestExecutionListeners} by appending them to the set of listeners used by
* this <code>TestContextManager</code>.
*/
public void registerTestExecutionListeners(TestExecutionListener... testExecutionListeners) {
for (TestExecutionListener listener : testExecutionListeners) {
@ -137,27 +145,26 @@ public class TestContextManager { @@ -137,27 +145,26 @@ public class TestContextManager {
}
/**
* Retrieves an array of newly instantiated
* {@link TestExecutionListener TestExecutionListeners} for the specified
* {@link Class class}. If
* {@link TestExecutionListeners @TestExecutionListeners} is not
* Retrieves an array of newly instantiated {@link TestExecutionListener
* TestExecutionListeners} for the specified {@link Class class}. If
* {@link TestExecutionListeners &#064;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</code>
* flag is set to <code>true</code>, listeners defined in the annotated
* class will be appended to the listeners defined in superclasses.
* @param clazz the Class object corresponding to the test class for which
* the listeners should be retrieved
* <p>
* Note that the {@link TestExecutionListeners#inheritListeners()
* inheritListeners} flag of {@link TestExecutionListeners
* &#064;TestExecutionListeners} will be taken into consideration.
* Specifically, if the <code>inheritListeners</code> flag is set to
* <code>true</code>, 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
*/
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>>();
List<Class<? extends TestExecutionListener>> classesList = new ArrayList<Class<? extends TestExecutionListener>>();
Class<?> declaringClass = AnnotationUtils.findAnnotationDeclaringClass(annotationType, clazz);
boolean defaultListeners = false;
@ -181,8 +188,9 @@ public class TestContextManager { @@ -181,8 +188,9 @@ public class TestContextManager {
if (classes != null) {
classesList.addAll(0, Arrays.<Class<? extends TestExecutionListener>> asList(classes));
}
declaringClass = (testExecutionListeners.inheritListeners() ?
AnnotationUtils.findAnnotationDeclaringClass(annotationType, declaringClass.getSuperclass()) : null);
declaringClass = (testExecutionListeners.inheritListeners() ? AnnotationUtils.findAnnotationDeclaringClass(
annotationType, declaringClass.getSuperclass())
: null);
}
}
@ -194,7 +202,8 @@ public class TestContextManager { @@ -194,7 +202,8 @@ public class TestContextManager {
catch (NoClassDefFoundError err) {
if (defaultListeners) {
if (logger.isDebugEnabled()) {
logger.debug("Could not instantiate default TestExecutionListener class [" + listenerClass.getName()
logger.debug("Could not instantiate default TestExecutionListener class ["
+ listenerClass.getName()
+ "]. Specify custom listener classes or make the default listener classes available.");
}
}
@ -211,12 +220,11 @@ public class TestContextManager { @@ -211,12 +220,11 @@ public class TestContextManager {
*/
@SuppressWarnings("unchecked")
protected Set<Class<? extends TestExecutionListener>> getDefaultTestExecutionListenerClasses() {
Set<Class<? extends TestExecutionListener>> defaultListenerClasses =
new LinkedHashSet<Class<? extends TestExecutionListener>>();
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));
defaultListenerClasses.add((Class<? extends TestExecutionListener>) getClass().getClassLoader().loadClass(
className));
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
@ -228,19 +236,23 @@ public class TestContextManager { @@ -228,19 +236,23 @@ public class TestContextManager {
return defaultListenerClasses;
}
/**
* Hook for preparing a test instance prior to execution of any individual
* test methods, for example for injecting dependencies, etc. Should be
* called immediately after instantiation of the test instance.
* <p>The managed {@link TestContext} will be updated with the supplied
* <p>
* The managed {@link TestContext} will be updated with the supplied
* <code>testInstance</code>.
* <p>An attempt will be made to give each registered
* <p>
* An attempt will be made to give each registered
* {@link TestExecutionListener} a chance to prepare the test instance. If a
* listener throws an exception, however, the remaining registered listeners
* will <strong>not</strong> be called.
* @param testInstance the test instance to prepare (never <code>null</code>)
* @throws Exception if a registered TestExecutionListener throws an exception
*
* @param testInstance the test instance to prepare (never <code>null</code>
* )
* @throws Exception if a registered TestExecutionListener throws an
* exception
* @see #getTestExecutionListeners()
*/
public void prepareTestInstance(Object testInstance) throws Exception {
@ -263,21 +275,25 @@ public class TestContextManager { @@ -263,21 +275,25 @@ public class TestContextManager {
}
/**
* Hook for pre-processing a test <em>before</em> execution of the
* supplied {@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} ).
* <p>The managed {@link TestContext} will be updated with the supplied
* Hook for pre-processing a test <em>before</em> execution of the supplied
* {@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 &#064;Before} ).
* <p>
* The managed {@link TestContext} will be updated with the supplied
* <code>testInstance</code> and <code>testMethod</code>.
* <p>An attempt will be made to give each registered
* <p>
* An attempt will be made to give each registered
* {@link TestExecutionListener} a chance to pre-process the test method
* execution. If a listener throws an exception, however, the remaining
* registered listeners will <strong>not</strong> be called.
*
* @param testInstance the current test instance (never <code>null</code>)
* @param testMethod the test method which is about to be executed on the
* test instance
* @throws Exception if a registered TestExecutionListener throws an exception
* @throws Exception if a registered TestExecutionListener throws an
* exception
* @see #getTestExecutionListeners()
*/
public void beforeTestMethod(Object testInstance, Method testMethod) throws Exception {
@ -301,41 +317,45 @@ public class TestContextManager { @@ -301,41 +317,45 @@ public class TestContextManager {
}
/**
* Hook for post-processing a test <em>after</em> execution of the
* supplied {@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}).
* <p>The managed {@link TestContext} will be updated with the supplied
* Hook for post-processing a test <em>after</em> execution of the supplied
* {@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 &#064;After}).
* <p>
* The managed {@link TestContext} will be updated with the supplied
* <code>testInstance</code>, <code>testMethod</code>, and
* <code>exception</code>.
* <p>Each registered {@link TestExecutionListener} will be given a chance to
* <p>
* Each registered {@link TestExecutionListener} will be given a chance to
* post-process the test method execution. If a listener throws an
* exception, the remaining registered listeners will still be called, but
* the first exception thrown will be tracked and rethrown after all
* listeners have executed. Note that registered listeners will be executed
* in the opposite order in which they were registered.
*
* @param testInstance the current test instance (never <code>null</code>)
* @param testMethod the test method which has just been executed on the
* test instance
* @param exception the exception that was thrown during execution of the
* test method or by a TestExecutionListener, or <code>null</code>
* if none was thrown
* @throws Exception if a registered TestExecutionListener throws an exception
* test method or by a TestExecutionListener, or <code>null</code> if none
* was thrown
* @throws Exception if a registered TestExecutionListener throws an
* exception
* @see #getTestExecutionListeners()
*/
public void afterTestMethod(Object testInstance, Method testMethod, Throwable exception) throws Exception {
Assert.notNull(testInstance, "testInstance must not be null");
if (logger.isTraceEnabled()) {
logger.trace("afterTestMethod(): instance [" + testInstance + "], method [" + testMethod +
"], exception [" + exception + "]");
logger.trace("afterTestMethod(): instance [" + testInstance + "], method [" + testMethod + "], exception ["
+ exception + "]");
}
getTestContext().updateState(testInstance, testMethod, exception);
// Traverse the TestExecutionListeners in reverse order to ensure proper
// "wrapper"-style execution of listeners.
List<TestExecutionListener> listenersReversed =
new ArrayList<TestExecutionListener>(getTestExecutionListeners());
List<TestExecutionListener> listenersReversed = new ArrayList<TestExecutionListener>(
getTestExecutionListeners());
Collections.reverse(listenersReversed);
Exception afterTestMethodException = null;
@ -344,9 +364,9 @@ public class TestContextManager { @@ -344,9 +364,9 @@ public class TestContextManager {
testExecutionListener.afterTestMethod(getTestContext());
}
catch (Exception ex) {
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
"] to process 'after' execution for test: method [" + testMethod + "], instance [" +
testInstance + "], exception [" + exception + "]", ex);
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener
+ "] to process 'after' execution for test: method [" + testMethod + "], instance ["
+ testInstance + "], exception [" + exception + "]", ex);
if (afterTestMethodException == null) {
afterTestMethodException = ex;
}

31
org.springframework.test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java

@ -94,7 +94,7 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { @@ -94,7 +94,7 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner {
* {@link TestContextManager} to provide Spring testing functionality to
* standard JUnit tests.
*
* @param clazz the Class object corresponding to the test class to be run
* @param clazz the test class to be run
* @see #createTestContextManager(Class)
*/
public SpringJUnit4ClassRunner(Class<?> clazz) throws InitializationError {
@ -106,14 +106,15 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { @@ -106,14 +106,15 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner {
}
/**
* Creates a new {@link TestContextManager}. Can be overridden by
* subclasses.
* Creates a new {@link TestContextManager} for the supplied test class and
* the configured <em>default <code>ContextLoader</code> class name</em>.
* Can be overridden by subclasses.
*
* @param clazz the Class object corresponding to the test class to be
* managed
* @param clazz the test class to be managed
* @see #getDefaultContextLoaderClassName(Class)
*/
protected TestContextManager createTestContextManager(Class<?> clazz) {
return new TestContextManager(clazz);
return new TestContextManager(clazz, getDefaultContextLoaderClassName(clazz));
}
/**
@ -123,6 +124,24 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { @@ -123,6 +124,24 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner {
return this.testContextManager;
}
/**
* Get the name of the default <code>ContextLoader</code> 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</code> class via the
* <code>&#064;ContextConfiguration</code> annotation.
* <p>
* The default implementation returns <code>null</code>, thus implying use
* of the <em>standard</em> default <code>ContextLoader</code> class name.
* Can be overridden by subclasses.
* </p>
*
* @param clazz the test class
* @return <code>null</code>
*/
protected String getDefaultContextLoaderClassName(Class<?> clazz) {
return null;
}
/**
* Delegates to the parent implementation for creating the test instance and
* then allows the {@link #getTestContextManager() TestContextManager} to

71
org.springframework.test/src/test/java/org/springframework/test/context/junit4/CustomDefaultContextLoaderClassSpringRunnerTests.java

@ -0,0 +1,71 @@ @@ -0,0 +1,71 @@
/*
* Copyright 2002-2009 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 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.beans.Pet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.support.GenericPropertiesContextLoader;
/**
* Unit 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.
*
* @author Sam Brannen
* @since 3.0
*/
@RunWith(CustomDefaultContextLoaderClassSpringRunnerTests.PropertiesBasedSpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests-context.properties")
public class CustomDefaultContextLoaderClassSpringRunnerTests {
@Autowired
private Pet cat;
@Autowired
private String testString;
@Test
public void verifyAnnotationAutowiredFields() {
assertNotNull("The cat field should have been autowired.", this.cat);
assertEquals("Garfield", this.cat.getName());
assertNotNull("The testString field should have been autowired.", this.testString);
assertEquals("Test String", this.testString);
}
public static final class PropertiesBasedSpringJUnit4ClassRunner extends SpringJUnit4ClassRunner {
public PropertiesBasedSpringJUnit4ClassRunner(Class<?> clazz) throws InitializationError {
super(clazz);
}
@Override
protected String getDefaultContextLoaderClassName(Class<?> clazz) {
return GenericPropertiesContextLoader.class.getName();
}
}
}

1
org.springframework.test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4SuiteTests.java

@ -57,6 +57,7 @@ StandardJUnit4FeaturesTests.class,// @@ -57,6 +57,7 @@ StandardJUnit4FeaturesTests.class,//
MultipleResourcesSpringJUnit4ClassRunnerAppCtxTests.class,//
InheritedConfigSpringJUnit4ClassRunnerAppCtxTests.class,//
PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests.class,//
CustomDefaultContextLoaderClassSpringRunnerTests.class,//
SpringRunnerContextCacheTests.class,//
ParameterizedDependencyInjectionTests.class,//
ClassLevelTransactionalSpringRunnerTests.class,//

Loading…
Cancel
Save