getTestExecutionListeners() returns actual List which allows for iteration as well as modification (SPR-7595)

This commit is contained in:
Juergen Hoeller 2010-10-02 01:05:40 +00:00
parent 40fa8afce4
commit 3f0462510e
1 changed files with 48 additions and 71 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2009 the original author or authors. * Copyright 2002-2010 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -26,6 +26,7 @@ import java.util.Set;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
@ -102,13 +103,10 @@ public class TestContextManager {
} }
/** /**
* Constructs a new <code>TestContextManager</code> for the specified * Constructs a new <code>TestContextManager</code> for the specified {@link Class test class}
* {@link Class test class} and automatically * and automatically {@link #registerTestExecutionListeners registers} the
* {@link #registerTestExecutionListeners(TestExecutionListener...) * {@link TestExecutionListener TestExecutionListeners} configured for the test class
* registers} the {@link TestExecutionListener TestExecutionListeners} * via the {@link TestExecutionListeners &#064;TestExecutionListeners} annotation.
* configured for the test class via the {@link TestExecutionListeners
* &#064;TestExecutionListeners} annotation.
*
* @param testClass the test class to be managed * @param testClass the test class to be managed
* @param defaultContextLoaderClassName the name of the default * @param defaultContextLoaderClassName the name of the default
* <code>ContextLoader</code> class to use (may be <code>null</code>) * <code>ContextLoader</code> class to use (may be <code>null</code>)
@ -128,54 +126,49 @@ public class TestContextManager {
return this.testContext; return this.testContext;
} }
/** /**
* Register the supplied {@link TestExecutionListener * Register the supplied {@link TestExecutionListener TestExecutionListeners}
* TestExecutionListeners} by appending them to the set of listeners used by * by appending them to the set of listeners used by this <code>TestContextManager</code>.
* this <code>TestContextManager</code>.
*/ */
public void registerTestExecutionListeners(TestExecutionListener... testExecutionListeners) { public void registerTestExecutionListeners(TestExecutionListener... testExecutionListeners) {
for (TestExecutionListener listener : testExecutionListeners) { for (TestExecutionListener listener : testExecutionListeners) {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Registering TestExecutionListener [" + listener + "]"); logger.trace("Registering TestExecutionListener: " + listener);
} }
this.testExecutionListeners.add(listener); this.testExecutionListeners.add(listener);
} }
} }
/** /**
* Gets an {@link Collections#unmodifiableList(List) unmodifiable} copy of * Get the current {@link TestExecutionListener TestExecutionListeners}
* the {@link TestExecutionListener TestExecutionListeners} registered for * registered for this <code>TestContextManager</code>.
* this <code>TestContextManager</code>. * <p>Allows for modifications, e.g. adding a listener to the beginning of the list.
* However, make sure to keep the list stable while actually executing tests.
*/ */
public final List<TestExecutionListener> getTestExecutionListeners() { public final List<TestExecutionListener> getTestExecutionListeners() {
return Collections.unmodifiableList(this.testExecutionListeners); return this.testExecutionListeners;
} }
/** /**
* Gets a copy of the {@link TestExecutionListener TestExecutionListeners} * Get a copy of the {@link TestExecutionListener TestExecutionListeners}
* registered for this <code>TestContextManager</code> in reverse order. * registered for this <code>TestContextManager</code> in reverse order.
*/ */
private List<TestExecutionListener> getReversedTestExecutionListeners() { private List<TestExecutionListener> getReversedTestExecutionListeners() {
List<TestExecutionListener> listenersReversed = new ArrayList<TestExecutionListener>( List<TestExecutionListener> listenersReversed =
getTestExecutionListeners()); new ArrayList<TestExecutionListener>(getTestExecutionListeners());
Collections.reverse(listenersReversed); Collections.reverse(listenersReversed);
return listenersReversed; return listenersReversed;
} }
/** /**
* Retrieves an array of newly instantiated {@link TestExecutionListener * Retrieve an array of newly instantiated {@link TestExecutionListener TestExecutionListeners}
* TestExecutionListeners} for the specified {@link Class class}. If * for the specified {@link Class class}. If {@link TestExecutionListeners &#064;TestExecutionListeners}
* {@link TestExecutionListeners &#064;TestExecutionListeners} is not * is not <em>present</em> on the supplied class, the default listeners will be returned.
* <em>present</em> on the supplied class, the default listeners will be * <p>Note that the {@link TestExecutionListeners#inheritListeners() inheritListeners} flag of
* returned. * {@link TestExecutionListeners &#064;TestExecutionListeners} will be taken into consideration.
* <p> * Specifically, if the <code>inheritListeners</code> flag is set to <code>true</code>, listeners
* Note that the {@link TestExecutionListeners#inheritListeners() * defined in the annotated class will be appended to the listeners defined in superclasses.
* 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 * @param clazz the test class for which the listeners should be retrieved
* @return an array of TestExecutionListeners for the specified class * @return an array of TestExecutionListeners for the specified class
*/ */
@ -207,7 +200,8 @@ public class TestContextManager {
Class<? extends TestExecutionListener>[] listenerClasses = testExecutionListeners.listeners(); Class<? extends TestExecutionListener>[] listenerClasses = testExecutionListeners.listeners();
if (!ObjectUtils.isEmpty(valueListenerClasses) && !ObjectUtils.isEmpty(listenerClasses)) { if (!ObjectUtils.isEmpty(valueListenerClasses) && !ObjectUtils.isEmpty(listenerClasses)) {
String msg = String.format( String msg = String.format(
"Test class [%s] has been configured with @TestExecutionListeners' 'value' [%s] and 'listeners' [%s] attributes. Use one or the other, but not both.", "Test class [%s] has been configured with @TestExecutionListeners' 'value' [%s] " +
"and 'listeners' [%s] attributes. Use one or the other, but not both.",
declaringClass, ObjectUtils.nullSafeToString(valueListenerClasses), declaringClass, ObjectUtils.nullSafeToString(valueListenerClasses),
ObjectUtils.nullSafeToString(listenerClasses)); ObjectUtils.nullSafeToString(listenerClasses));
logger.error(msg); logger.error(msg);
@ -220,16 +214,15 @@ public class TestContextManager {
if (listenerClasses != null) { if (listenerClasses != null) {
classesList.addAll(0, Arrays.<Class<? extends TestExecutionListener>> asList(listenerClasses)); classesList.addAll(0, Arrays.<Class<? extends TestExecutionListener>> asList(listenerClasses));
} }
declaringClass = (testExecutionListeners.inheritListeners() ? AnnotationUtils.findAnnotationDeclaringClass( declaringClass = (testExecutionListeners.inheritListeners() ?
annotationType, declaringClass.getSuperclass()) AnnotationUtils.findAnnotationDeclaringClass(annotationType, declaringClass.getSuperclass()) : null);
: null);
} }
} }
List<TestExecutionListener> listeners = new ArrayList<TestExecutionListener>(classesList.size()); List<TestExecutionListener> listeners = new ArrayList<TestExecutionListener>(classesList.size());
for (Class<? extends TestExecutionListener> listenerClass : classesList) { for (Class<? extends TestExecutionListener> listenerClass : classesList) {
try { try {
listeners.add((TestExecutionListener) BeanUtils.instantiateClass(listenerClass)); listeners.add(BeanUtils.instantiateClass(listenerClass));
} }
catch (NoClassDefFoundError err) { catch (NoClassDefFoundError err) {
if (defaultListeners) { if (defaultListeners) {
@ -252,11 +245,12 @@ public class TestContextManager {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected Set<Class<? extends TestExecutionListener>> getDefaultTestExecutionListenerClasses() { 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) { for (String className : DEFAULT_TEST_EXECUTION_LISTENER_CLASS_NAMES) {
try { try {
defaultListenerClasses.add((Class<? extends TestExecutionListener>) getClass().getClassLoader().loadClass( defaultListenerClasses.add(
className)); (Class<? extends TestExecutionListener>) getClass().getClassLoader().loadClass(className));
} }
catch (Throwable ex) { catch (Throwable ex) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
@ -268,17 +262,16 @@ public class TestContextManager {
return defaultListenerClasses; return defaultListenerClasses;
} }
/** /**
* Hook for pre-processing a test class <em>before</em> execution of any * Hook for pre-processing a test class <em>before</em> execution of any
* tests within the class. Should be called prior to any framework-specific * tests within the class. Should be called prior to any framework-specific
* <em>before class methods</em> (e.g., methods annotated with JUnit's * <em>before class methods</em> (e.g., methods annotated with JUnit's
* {@link org.junit.BeforeClass &#064;BeforeClass}). * {@link org.junit.BeforeClass &#064;BeforeClass}).
* <p> * <p>An attempt will be made to give each registered
* An attempt will be made to give each registered
* {@link TestExecutionListener} a chance to pre-process the test class * {@link TestExecutionListener} a chance to pre-process the test class
* execution. If a listener throws an exception, however, the remaining * execution. If a listener throws an exception, however, the remaining
* registered listeners will <strong>not</strong> be called. * registered listeners will <strong>not</strong> be called.
*
* @throws Exception if a registered TestExecutionListener throws an * @throws Exception if a registered TestExecutionListener throws an
* exception * exception
* @see #getTestExecutionListeners() * @see #getTestExecutionListeners()
@ -306,19 +299,14 @@ public class TestContextManager {
* Hook for preparing a test instance prior to execution of any individual * Hook for preparing a test instance prior to execution of any individual
* test methods, for example for injecting dependencies, etc. Should be * test methods, for example for injecting dependencies, etc. Should be
* called immediately after instantiation of the test instance. * called immediately after instantiation of the test instance.
* <p> * <p>The managed {@link TestContext} will be updated with the supplied
* The managed {@link TestContext} will be updated with the supplied
* <code>testInstance</code>. * <code>testInstance</code>.
* <p> * <p>An attempt will be made to give each registered
* An attempt will be made to give each registered
* {@link TestExecutionListener} a chance to prepare the test instance. If a * {@link TestExecutionListener} a chance to prepare the test instance. If a
* listener throws an exception, however, the remaining registered listeners * listener throws an exception, however, the remaining registered listeners
* will <strong>not</strong> be called. * will <strong>not</strong> be called.
* * @param testInstance the test instance to prepare (never <code>null</code>)
* @param testInstance the test instance to prepare (never <code>null</code> * @throws Exception if a registered TestExecutionListener throws an exception
* )
* @throws Exception if a registered TestExecutionListener throws an
* exception
* @see #getTestExecutionListeners() * @see #getTestExecutionListeners()
*/ */
public void prepareTestInstance(Object testInstance) throws Exception { public void prepareTestInstance(Object testInstance) throws Exception {
@ -346,20 +334,16 @@ public class TestContextManager {
* starting a transaction, etc. Should be called prior to any * starting a transaction, etc. Should be called prior to any
* framework-specific <em>before methods</em> (e.g., methods annotated with * framework-specific <em>before methods</em> (e.g., methods annotated with
* JUnit's {@link org.junit.Before &#064;Before}). * JUnit's {@link org.junit.Before &#064;Before}).
* <p> * <p>The managed {@link TestContext} will be updated with the supplied
* The managed {@link TestContext} will be updated with the supplied
* <code>testInstance</code> and <code>testMethod</code>. * <code>testInstance</code> and <code>testMethod</code>.
* <p> * <p>An attempt will be made to give each registered
* An attempt will be made to give each registered
* {@link TestExecutionListener} a chance to pre-process the test method * {@link TestExecutionListener} a chance to pre-process the test method
* execution. If a listener throws an exception, however, the remaining * execution. If a listener throws an exception, however, the remaining
* registered listeners will <strong>not</strong> be called. * registered listeners will <strong>not</strong> be called.
*
* @param testInstance the current test instance (never <code>null</code>) * @param testInstance the current test instance (never <code>null</code>)
* @param testMethod the test method which is about to be executed on the * @param testMethod the test method which is about to be executed on the
* test instance * test instance
* @throws Exception if a registered TestExecutionListener throws an * @throws Exception if a registered TestExecutionListener throws an exception
* exception
* @see #getTestExecutionListeners() * @see #getTestExecutionListeners()
*/ */
public void beforeTestMethod(Object testInstance, Method testMethod) throws Exception { public void beforeTestMethod(Object testInstance, Method testMethod) throws Exception {
@ -388,26 +372,22 @@ public class TestContextManager {
* ending a transaction, etc. Should be called after any framework-specific * ending a transaction, etc. Should be called after any framework-specific
* <em>after methods</em> (e.g., methods annotated with JUnit's * <em>after methods</em> (e.g., methods annotated with JUnit's
* {@link org.junit.After &#064;After}). * {@link org.junit.After &#064;After}).
* <p> * <p>The managed {@link TestContext} will be updated with the supplied
* The managed {@link TestContext} will be updated with the supplied
* <code>testInstance</code>, <code>testMethod</code>, and * <code>testInstance</code>, <code>testMethod</code>, and
* <code>exception</code>. * <code>exception</code>.
* <p> * <p>Each registered {@link TestExecutionListener} will be given a chance to
* Each registered {@link TestExecutionListener} will be given a chance to
* post-process the test method execution. If a listener throws an * post-process the test method execution. If a listener throws an
* exception, the remaining registered listeners will still be called, but * exception, the remaining registered listeners will still be called, but
* the first exception thrown will be tracked and rethrown after all * the first exception thrown will be tracked and rethrown after all
* listeners have executed. Note that registered listeners will be executed * listeners have executed. Note that registered listeners will be executed
* in the opposite order in which they were registered. * in the opposite order in which they were registered.
*
* @param testInstance the current test instance (never <code>null</code>) * @param testInstance the current test instance (never <code>null</code>)
* @param testMethod the test method which has just been executed on the * @param testMethod the test method which has just been executed on the
* test instance * test instance
* @param exception the exception that was thrown during execution of the * @param exception the exception that was thrown during execution of the
* test method or by a TestExecutionListener, or <code>null</code> if none * test method or by a TestExecutionListener, or <code>null</code> if none
* was thrown * was thrown
* @throws Exception if a registered TestExecutionListener throws an * @throws Exception if a registered TestExecutionListener throws an exception
* exception
* @see #getTestExecutionListeners() * @see #getTestExecutionListeners()
*/ */
public void afterTestMethod(Object testInstance, Method testMethod, Throwable exception) throws Exception { public void afterTestMethod(Object testInstance, Method testMethod, Throwable exception) throws Exception {
@ -444,16 +424,13 @@ public class TestContextManager {
* tests within the class. Should be called after any framework-specific * tests within the class. Should be called after any framework-specific
* <em>after class methods</em> (e.g., methods annotated with JUnit's * <em>after class methods</em> (e.g., methods annotated with JUnit's
* {@link org.junit.AfterClass &#064;AfterClass}). * {@link org.junit.AfterClass &#064;AfterClass}).
* <p> * <p>Each registered {@link TestExecutionListener} will be given a chance to
* Each registered {@link TestExecutionListener} will be given a chance to
* post-process the test class. If a listener throws an exception, the * post-process the test class. If a listener throws an exception, the
* remaining registered listeners will still be called, but the first * remaining registered listeners will still be called, but the first
* exception thrown will be tracked and rethrown after all listeners have * exception thrown will be tracked and rethrown after all listeners have
* executed. Note that registered listeners will be executed in the opposite * executed. Note that registered listeners will be executed in the opposite
* order in which they were registered. * order in which they were registered.
* * @throws Exception if a registered TestExecutionListener throws an exception
* @throws Exception if a registered TestExecutionListener throws an
* exception
* @see #getTestExecutionListeners() * @see #getTestExecutionListeners()
*/ */
public void afterTestClass() throws Exception { public void afterTestClass() throws Exception {