Introduce before/after test execution callbacks in TestContextManager
Issue: SPR-4365
This commit is contained in:
parent
5302566cbb
commit
087efa668c
|
@ -40,23 +40,29 @@ import org.springframework.util.ReflectionUtils;
|
||||||
* <li>{@link #beforeTestClass() before test class execution}: prior to any
|
* <li>{@link #beforeTestClass() before test class execution}: prior to any
|
||||||
* <em>before class callbacks</em> of a particular testing framework (e.g.,
|
* <em>before class callbacks</em> of a particular testing framework (e.g.,
|
||||||
* JUnit 4's {@link org.junit.BeforeClass @BeforeClass})</li>
|
* JUnit 4's {@link org.junit.BeforeClass @BeforeClass})</li>
|
||||||
* <li>{@link #prepareTestInstance(Object) test instance preparation}:
|
* <li>{@link #prepareTestInstance test instance preparation}:
|
||||||
* immediately following instantiation of the test instance</li>
|
* immediately following instantiation of the test class</li>
|
||||||
* <li>{@link #beforeTestMethod(Object, Method) before test method execution}:
|
* <li>{@link #beforeTestMethod before test setup}:
|
||||||
* prior to any <em>before method callbacks</em> of a particular testing framework
|
* prior to any <em>before method callbacks</em> of a particular testing framework
|
||||||
* (e.g., JUnit 4's {@link org.junit.Before @Before})</li>
|
* (e.g., JUnit 4's {@link org.junit.Before @Before})</li>
|
||||||
* <li>{@link #afterTestMethod(Object, Method, Throwable) after test method
|
* <li>{@link #beforeTestExecution before test execution}:
|
||||||
* execution}: after any <em>after method callbacks</em> of a particular testing
|
* immediately before execution of the {@linkplain java.lang.reflect.Method
|
||||||
|
* test method} but after test setup</li>
|
||||||
|
* <li>{@link #afterTestExecution after test execution}:
|
||||||
|
* immediately after execution of the {@linkplain java.lang.reflect.Method
|
||||||
|
* test method} but before test tear down</li>
|
||||||
|
* <li>{@link #afterTestMethod(Object, Method, Throwable) after test tear down}:
|
||||||
|
* after any <em>after method callbacks</em> of a particular testing
|
||||||
* framework (e.g., JUnit 4's {@link org.junit.After @After})</li>
|
* framework (e.g., JUnit 4's {@link org.junit.After @After})</li>
|
||||||
* <li>{@link #afterTestClass() after test class execution}: after any
|
* <li>{@link #afterTestClass() after test class execution}: after any
|
||||||
* <em>after class callbacks</em> of a particular testing framework (e.g., JUnit
|
* <em>after class callbacks</em> of a particular testing framework (e.g., JUnit 4's
|
||||||
* 4's {@link org.junit.AfterClass @AfterClass})</li>
|
* {@link org.junit.AfterClass @AfterClass})</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>Support for loading and accessing
|
* <p>Support for loading and accessing
|
||||||
* {@link org.springframework.context.ApplicationContext application contexts},
|
* {@linkplain org.springframework.context.ApplicationContext application contexts},
|
||||||
* dependency injection of test instances,
|
* dependency injection of test instances,
|
||||||
* {@link org.springframework.transaction.annotation.Transactional transactional}
|
* {@linkplain org.springframework.transaction.annotation.Transactional transactional}
|
||||||
* execution of test methods, etc. is provided by
|
* execution of test methods, etc. is provided by
|
||||||
* {@link SmartContextLoader ContextLoaders} and {@link TestExecutionListener
|
* {@link SmartContextLoader ContextLoaders} and {@link TestExecutionListener
|
||||||
* TestExecutionListeners}, which are configured via
|
* TestExecutionListeners}, which are configured via
|
||||||
|
@ -173,7 +179,7 @@ public class TestContextManager {
|
||||||
/**
|
/**
|
||||||
* 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 4's
|
||||||
* {@link org.junit.BeforeClass @BeforeClass}).
|
* {@link org.junit.BeforeClass @BeforeClass}).
|
||||||
* <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 class
|
* {@link TestExecutionListener} a chance to pre-process the test class
|
||||||
|
@ -181,6 +187,7 @@ public class TestContextManager {
|
||||||
* 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
|
||||||
|
* @since 3.0
|
||||||
* @see #getTestExecutionListeners()
|
* @see #getTestExecutionListeners()
|
||||||
*/
|
*/
|
||||||
public void beforeTestClass() throws Exception {
|
public void beforeTestClass() throws Exception {
|
||||||
|
@ -195,10 +202,7 @@ public class TestContextManager {
|
||||||
testExecutionListener.beforeTestClass(getTestContext());
|
testExecutionListener.beforeTestClass(getTestContext());
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
if (logger.isWarnEnabled()) {
|
logException(ex, "beforeTestClass", testExecutionListener, testClass);
|
||||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
|
|
||||||
"] to process 'before class' callback for test class [" + testClass + "]", ex);
|
|
||||||
}
|
|
||||||
ReflectionUtils.rethrowException(ex);
|
ReflectionUtils.rethrowException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -240,60 +244,101 @@ public class TestContextManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook for pre-processing a test <em>before</em> execution of the supplied
|
* Hook for pre-processing a test <em>before</em> execution of <em>before</em>
|
||||||
* {@link Method test method}, for example for setting up test fixtures,
|
* lifecycle callbacks of the underlying test framework — for example,
|
||||||
* starting a transaction, etc. Should be called prior to any
|
* setting up test fixtures, starting a transaction, etc.
|
||||||
* framework-specific <em>before methods</em> (e.g., methods annotated with
|
* <p>This method <strong>must</strong> be called immediately prior to
|
||||||
* JUnit's {@link org.junit.Before @Before}).
|
* framework-specific <em>before</em> lifecycle callbacks (e.g., methods
|
||||||
|
* annotated with JUnit 4's {@link org.junit.Before @Before}). For historical
|
||||||
|
* reasons, this method is named {@code beforeTestMethod}. Since the
|
||||||
|
* introduction of {@link #beforeTestExecution}, a more suitable name for
|
||||||
|
* this method might be something like {@code beforeTestSetUp} or
|
||||||
|
* {@code beforeEach}; however, it is unfortunately impossible to rename
|
||||||
|
* this method due to backward compatibility concerns.
|
||||||
* <p>The managed {@link TestContext} will be updated with the supplied
|
* <p>The managed {@link TestContext} will be updated with the supplied
|
||||||
* {@code testInstance} and {@code testMethod}.
|
* {@code testInstance} and {@code testMethod}.
|
||||||
* <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
|
* {@link TestExecutionListener} a chance to perform its pre-processing.
|
||||||
* execution. If a listener throws an exception, however, the remaining
|
* If a listener throws an exception, however, the remaining registered
|
||||||
* registered listeners will <strong>not</strong> be called.
|
* listeners will <strong>not</strong> be called.
|
||||||
* @param testInstance the current test instance (never {@code null})
|
* @param testInstance the current test instance (never {@code null})
|
||||||
* @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 exception
|
* @throws Exception if a registered TestExecutionListener throws an exception
|
||||||
|
* @see #afterTestMethod
|
||||||
|
* @see #beforeTestExecution
|
||||||
|
* @see #afterTestExecution
|
||||||
* @see #getTestExecutionListeners()
|
* @see #getTestExecutionListeners()
|
||||||
*/
|
*/
|
||||||
public void beforeTestMethod(Object testInstance, Method testMethod) throws Exception {
|
public void beforeTestMethod(Object testInstance, Method testMethod) throws Exception {
|
||||||
Assert.notNull(testInstance, "Test instance must not be null");
|
String callbackName = "beforeTestMethod";
|
||||||
if (logger.isTraceEnabled()) {
|
prepareForBeforeCallback(callbackName, testInstance, testMethod);
|
||||||
logger.trace("beforeTestMethod(): instance [" + testInstance + "], method [" + testMethod + "]");
|
|
||||||
}
|
|
||||||
getTestContext().updateState(testInstance, testMethod, null);
|
|
||||||
|
|
||||||
for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {
|
for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {
|
||||||
try {
|
try {
|
||||||
testExecutionListener.beforeTestMethod(getTestContext());
|
testExecutionListener.beforeTestMethod(getTestContext());
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
if (logger.isWarnEnabled()) {
|
handleBeforeException(ex, callbackName, testExecutionListener, testInstance, testMethod);
|
||||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
|
|
||||||
"] to process 'before' execution of test method [" + testMethod + "] for test instance [" +
|
|
||||||
testInstance + "]", ex);
|
|
||||||
}
|
|
||||||
ReflectionUtils.rethrowException(ex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook for post-processing a test <em>after</em> execution of the supplied
|
* Hook for pre-processing a test <em>immediately before</em> execution of
|
||||||
* {@link Method test method}, for example for tearing down test fixtures,
|
* the {@linkplain java.lang.reflect.Method test method} in the supplied
|
||||||
* ending a transaction, etc. Should be called after any framework-specific
|
* {@linkplain TestContext test context} — for example, for timing
|
||||||
* <em>after methods</em> (e.g., methods annotated with JUnit's
|
* or logging purposes.
|
||||||
|
* <p>This method <strong>must</strong> be called after framework-specific
|
||||||
|
* <em>before</em> lifecycle callbacks (e.g., methods annotated with JUnit 4's
|
||||||
|
* {@link org.junit.Before @Before}).
|
||||||
|
* <p>The managed {@link TestContext} will be updated with the supplied
|
||||||
|
* {@code testInstance} and {@code testMethod}.
|
||||||
|
* <p>An attempt will be made to give each registered
|
||||||
|
* {@link TestExecutionListener} a chance to perform its pre-processing.
|
||||||
|
* 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})
|
||||||
|
* @param testMethod the test method which is about to be executed on the
|
||||||
|
* test instance
|
||||||
|
* @throws Exception if a registered TestExecutionListener throws an exception
|
||||||
|
* @since 5.0
|
||||||
|
* @see #beforeTestMethod
|
||||||
|
* @see #afterTestMethod
|
||||||
|
* @see #beforeTestExecution
|
||||||
|
* @see #afterTestExecution
|
||||||
|
* @see #getTestExecutionListeners()
|
||||||
|
*/
|
||||||
|
public void beforeTestExecution(Object testInstance, Method testMethod) throws Exception {
|
||||||
|
String callbackName = "beforeTestExecution";
|
||||||
|
prepareForBeforeCallback(callbackName, testInstance, testMethod);
|
||||||
|
|
||||||
|
for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {
|
||||||
|
try {
|
||||||
|
testExecutionListener.beforeTestExecution(getTestContext());
|
||||||
|
}
|
||||||
|
catch (Throwable ex) {
|
||||||
|
handleBeforeException(ex, callbackName, testExecutionListener, testInstance, testMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for post-processing a test <em>immediately after</em> execution of
|
||||||
|
* the {@linkplain java.lang.reflect.Method test method} in the supplied
|
||||||
|
* {@linkplain TestContext test context} — for example, for timing
|
||||||
|
* or logging purposes.
|
||||||
|
* <p>This method <strong>must</strong> be called before framework-specific
|
||||||
|
* <em>after</em> lifecycle callbacks (e.g., methods annotated with JUnit 4's
|
||||||
* {@link org.junit.After @After}).
|
* {@link org.junit.After @After}).
|
||||||
* <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 testMethod}, and
|
* {@code testInstance}, {@code testMethod}, and {@code exception}.
|
||||||
* {@code exception}.
|
* <p>Each registered {@link TestExecutionListener} will be given a chance
|
||||||
* <p>Each registered {@link TestExecutionListener} will be given a chance to
|
* to perform its post-processing. If a listener throws an exception, the
|
||||||
* post-process the test method execution. If a listener throws an
|
* remaining registered listeners will still be called, but the first
|
||||||
* exception, the remaining registered listeners will still be called, but
|
* exception thrown will be tracked and rethrown after all listeners have
|
||||||
* the first exception thrown will be tracked and rethrown after all
|
* executed. Note that registered listeners will be executed in the opposite
|
||||||
* listeners have executed. Note that registered listeners will be executed
|
* order in which they were registered.
|
||||||
* in the opposite order in which they were registered.
|
|
||||||
* @param testInstance the current test instance (never {@code null})
|
* @param testInstance the current test instance (never {@code null})
|
||||||
* @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
|
||||||
|
@ -301,15 +346,70 @@ public class TestContextManager {
|
||||||
* test method or by a TestExecutionListener, or {@code null} if none
|
* test method or by a TestExecutionListener, or {@code null} if none
|
||||||
* was thrown
|
* was thrown
|
||||||
* @throws Exception if a registered TestExecutionListener throws an exception
|
* @throws Exception if a registered TestExecutionListener throws an exception
|
||||||
|
* @since 5.0
|
||||||
|
* @see #beforeTestMethod
|
||||||
|
* @see #afterTestMethod
|
||||||
|
* @see #beforeTestExecution
|
||||||
|
* @see #getTestExecutionListeners()
|
||||||
|
*/
|
||||||
|
public void afterTestExecution(Object testInstance, Method testMethod, Throwable exception) throws Exception {
|
||||||
|
String callbackName = "afterTestExecution";
|
||||||
|
prepareForAfterCallback(callbackName, testInstance, testMethod, exception);
|
||||||
|
|
||||||
|
Throwable afterTestExecutionException = null;
|
||||||
|
// Traverse the TestExecutionListeners in reverse order to ensure proper
|
||||||
|
// "wrapper"-style execution of listeners.
|
||||||
|
for (TestExecutionListener testExecutionListener : getReversedTestExecutionListeners()) {
|
||||||
|
try {
|
||||||
|
testExecutionListener.afterTestExecution(getTestContext());
|
||||||
|
}
|
||||||
|
catch (Throwable ex) {
|
||||||
|
logException(ex, callbackName, testExecutionListener, testInstance, testMethod);
|
||||||
|
if (afterTestExecutionException == null) {
|
||||||
|
afterTestExecutionException = ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (afterTestExecutionException != null) {
|
||||||
|
ReflectionUtils.rethrowException(afterTestExecutionException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for post-processing a test <em>after</em> execution of <em>after</em>
|
||||||
|
* lifecycle callbacks of the underlying test framework — for example,
|
||||||
|
* tearing down test fixtures, ending a transaction, etc.
|
||||||
|
* <p>This method <strong>must</strong> be called immediately after
|
||||||
|
* framework-specific <em>after</em> lifecycle callbacks (e.g., methods
|
||||||
|
* annotated with JUnit 4's {@link org.junit.After @After}). For historical
|
||||||
|
* reasons, this method is named {@code afterTestMethod}. Since the
|
||||||
|
* introduction of {@link #afterTestExecution}, a more suitable name for
|
||||||
|
* this method might be something like {@code afterTestTearDown} or
|
||||||
|
* {@code afterEach}; however, it is unfortunately impossible to rename
|
||||||
|
* this method due to backward compatibility concerns.
|
||||||
|
* <p>The managed {@link TestContext} will be updated with the supplied
|
||||||
|
* {@code testInstance}, {@code testMethod}, and {@code exception}.
|
||||||
|
* <p>Each registered {@link TestExecutionListener} will be given a chance
|
||||||
|
* to perform its post-processing. 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})
|
||||||
|
* @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} if none
|
||||||
|
* was thrown
|
||||||
|
* @throws Exception if a registered TestExecutionListener throws an exception
|
||||||
|
* @see #beforeTestMethod
|
||||||
|
* @see #beforeTestExecution
|
||||||
|
* @see #afterTestExecution
|
||||||
* @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 {
|
||||||
Assert.notNull(testInstance, "Test instance must not be null");
|
String callbackName = "afterTestMethod";
|
||||||
if (logger.isTraceEnabled()) {
|
prepareForAfterCallback(callbackName, testInstance, testMethod, exception);
|
||||||
logger.trace("afterTestMethod(): instance [" + testInstance + "], method [" + testMethod +
|
|
||||||
"], exception [" + exception + "]");
|
|
||||||
}
|
|
||||||
getTestContext().updateState(testInstance, testMethod, exception);
|
|
||||||
|
|
||||||
Throwable afterTestMethodException = null;
|
Throwable afterTestMethodException = null;
|
||||||
// Traverse the TestExecutionListeners in reverse order to ensure proper
|
// Traverse the TestExecutionListeners in reverse order to ensure proper
|
||||||
|
@ -319,11 +419,7 @@ public class TestContextManager {
|
||||||
testExecutionListener.afterTestMethod(getTestContext());
|
testExecutionListener.afterTestMethod(getTestContext());
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
if (logger.isWarnEnabled()) {
|
logException(ex, callbackName, testExecutionListener, testInstance, testMethod);
|
||||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
|
|
||||||
"] to process 'after' execution for test: method [" + testMethod + "], instance [" +
|
|
||||||
testInstance + "], exception [" + exception + "]", ex);
|
|
||||||
}
|
|
||||||
if (afterTestMethodException == null) {
|
if (afterTestMethodException == null) {
|
||||||
afterTestMethodException = ex;
|
afterTestMethodException = ex;
|
||||||
}
|
}
|
||||||
|
@ -337,7 +433,7 @@ public class TestContextManager {
|
||||||
/**
|
/**
|
||||||
* Hook for post-processing a test class <em>after</em> execution of all
|
* Hook for post-processing a test class <em>after</em> execution of all
|
||||||
* 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 4's
|
||||||
* {@link org.junit.AfterClass @AfterClass}).
|
* {@link org.junit.AfterClass @AfterClass}).
|
||||||
* <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 class. If a listener throws an exception, the
|
* post-process the test class. If a listener throws an exception, the
|
||||||
|
@ -346,6 +442,7 @@ public class TestContextManager {
|
||||||
* 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
|
||||||
|
* @since 3.0
|
||||||
* @see #getTestExecutionListeners()
|
* @see #getTestExecutionListeners()
|
||||||
*/
|
*/
|
||||||
public void afterTestClass() throws Exception {
|
public void afterTestClass() throws Exception {
|
||||||
|
@ -363,10 +460,7 @@ public class TestContextManager {
|
||||||
testExecutionListener.afterTestClass(getTestContext());
|
testExecutionListener.afterTestClass(getTestContext());
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
if (logger.isWarnEnabled()) {
|
logException(ex, "afterTestClass", testExecutionListener, testClass);
|
||||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
|
|
||||||
"] to process 'after class' callback for test class [" + testClass + "]", ex);
|
|
||||||
}
|
|
||||||
if (afterTestClassException == null) {
|
if (afterTestClassException == null) {
|
||||||
afterTestClassException = ex;
|
afterTestClassException = ex;
|
||||||
}
|
}
|
||||||
|
@ -377,4 +471,46 @@ public class TestContextManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void prepareForBeforeCallback(String callbackName, Object testInstance, Method testMethod) {
|
||||||
|
Assert.notNull(testInstance, "Test instance must not be null");
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace(String.format("%s(): instance [%s], method [%s]", callbackName, testInstance, testMethod));
|
||||||
|
}
|
||||||
|
getTestContext().updateState(testInstance, testMethod, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareForAfterCallback(String callbackName, Object testInstance, Method testMethod,
|
||||||
|
Throwable exception) {
|
||||||
|
Assert.notNull(testInstance, "Test instance must not be null");
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace(String.format("%s(): instance [%s], method [%s], exception [%s]", callbackName, testInstance,
|
||||||
|
testMethod, exception));
|
||||||
|
}
|
||||||
|
getTestContext().updateState(testInstance, testMethod, exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleBeforeException(Throwable ex, String callbackName, TestExecutionListener testExecutionListener,
|
||||||
|
Object testInstance, Method testMethod) throws Exception {
|
||||||
|
logException(ex, callbackName, testExecutionListener, testInstance, testMethod);
|
||||||
|
ReflectionUtils.rethrowException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logException(Throwable ex, String callbackName, TestExecutionListener testExecutionListener,
|
||||||
|
Class<?> testClass) {
|
||||||
|
if (logger.isWarnEnabled()) {
|
||||||
|
logger.warn(String.format("Caught exception while invoking '%s' callback on " +
|
||||||
|
"TestExecutionListener [%s] for test class [%s]", callbackName, testExecutionListener,
|
||||||
|
testClass), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logException(Throwable ex, String callbackName, TestExecutionListener testExecutionListener,
|
||||||
|
Object testInstance, Method testMethod) {
|
||||||
|
if (logger.isWarnEnabled()) {
|
||||||
|
logger.warn(String.format("Caught exception while invoking '%s' callback on " +
|
||||||
|
"TestExecutionListener [%s] for test method [%s] and test instance [%s]",
|
||||||
|
callbackName, testExecutionListener, testMethod, testInstance), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,17 +21,8 @@ import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.AfterClass;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.core.style.ToStringCreator;
|
|
||||||
import org.springframework.test.context.support.AbstractTestExecutionListener;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,94 +35,77 @@ import static org.junit.Assert.*;
|
||||||
*/
|
*/
|
||||||
public class TestContextManagerTests {
|
public class TestContextManagerTests {
|
||||||
|
|
||||||
private static final String FIRST = "veni";
|
private static final List<String> executionOrder = new ArrayList<>();
|
||||||
private static final String SECOND = "vidi";
|
|
||||||
private static final String THIRD = "vici";
|
|
||||||
|
|
||||||
private static final List<String> afterTestMethodCalls = new ArrayList<>();
|
|
||||||
private static final List<String> beforeTestMethodCalls = new ArrayList<>();
|
|
||||||
|
|
||||||
protected static final Log logger = LogFactory.getLog(TestContextManagerTests.class);
|
|
||||||
|
|
||||||
private final TestContextManager testContextManager = new TestContextManager(ExampleTestCase.class);
|
private final TestContextManager testContextManager = new TestContextManager(ExampleTestCase.class);
|
||||||
|
|
||||||
|
private final Method testMethod;
|
||||||
private Method getTestMethod() throws NoSuchMethodException {
|
{
|
||||||
return ExampleTestCase.class.getDeclaredMethod("exampleTestMethod", (Class<?>[]) null);
|
try {
|
||||||
|
this.testMethod = ExampleTestCase.class.getDeclaredMethod("exampleTestMethod");
|
||||||
}
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
/**
|
throw new RuntimeException(ex);
|
||||||
* Asserts the <em>execution order</em> of 'before' and 'after' test method
|
|
||||||
* calls on {@link TestExecutionListener listeners} registered for the
|
|
||||||
* configured {@link TestContextManager}.
|
|
||||||
*
|
|
||||||
* @see #beforeTestMethodCalls
|
|
||||||
* @see #afterTestMethodCalls
|
|
||||||
*/
|
|
||||||
private static void assertExecutionOrder(List<String> expectedBeforeTestMethodCalls,
|
|
||||||
List<String> expectedAfterTestMethodCalls, final String usageContext) {
|
|
||||||
|
|
||||||
if (expectedBeforeTestMethodCalls == null) {
|
|
||||||
expectedBeforeTestMethodCalls = new ArrayList<>();
|
|
||||||
}
|
|
||||||
if (expectedAfterTestMethodCalls == null) {
|
|
||||||
expectedAfterTestMethodCalls = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
for (String listenerName : beforeTestMethodCalls) {
|
|
||||||
logger.debug("'before' listener [" + listenerName + "] (" + usageContext + ").");
|
|
||||||
}
|
|
||||||
for (String listenerName : afterTestMethodCalls) {
|
|
||||||
logger.debug("'after' listener [" + listenerName + "] (" + usageContext + ").");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue("Verifying execution order of 'before' listeners' (" + usageContext + ").",
|
|
||||||
expectedBeforeTestMethodCalls.equals(beforeTestMethodCalls));
|
|
||||||
assertTrue("Verifying execution order of 'after' listeners' (" + usageContext + ").",
|
|
||||||
expectedAfterTestMethodCalls.equals(afterTestMethodCalls));
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeClass
|
|
||||||
public static void setUpBeforeClass() throws Exception {
|
|
||||||
beforeTestMethodCalls.clear();
|
|
||||||
afterTestMethodCalls.clear();
|
|
||||||
assertExecutionOrder(null, null, "BeforeClass");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies the expected {@link TestExecutionListener}
|
|
||||||
* <em>execution order</em> after all test methods have completed.
|
|
||||||
*/
|
|
||||||
@AfterClass
|
|
||||||
public static void verifyListenerExecutionOrderAfterClass() throws Exception {
|
|
||||||
assertExecutionOrder(Arrays.<String> asList(FIRST, SECOND, THIRD),
|
|
||||||
Arrays.<String> asList(THIRD, SECOND, FIRST), "AfterClass");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUpTestContextManager() throws Throwable {
|
|
||||||
assertEquals("Verifying the number of registered TestExecutionListeners.", 3,
|
|
||||||
this.testContextManager.getTestExecutionListeners().size());
|
|
||||||
|
|
||||||
this.testContextManager.beforeTestMethod(new ExampleTestCase(), getTestMethod());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies the expected {@link TestExecutionListener}
|
|
||||||
* <em>execution order</em> within a test method.
|
|
||||||
*
|
|
||||||
* @see #verifyListenerExecutionOrderAfterClass()
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void verifyListenerExecutionOrderWithinTestMethod() {
|
public void listenerExecutionOrder() throws Exception {
|
||||||
assertExecutionOrder(Arrays.<String> asList(FIRST, SECOND, THIRD), null, "Test");
|
// @formatter:off
|
||||||
|
assertEquals("Registered TestExecutionListeners", 3, this.testContextManager.getTestExecutionListeners().size());
|
||||||
|
|
||||||
|
this.testContextManager.beforeTestMethod(this, this.testMethod);
|
||||||
|
assertExecutionOrder("beforeTestMethod",
|
||||||
|
"beforeTestMethod-1",
|
||||||
|
"beforeTestMethod-2",
|
||||||
|
"beforeTestMethod-3"
|
||||||
|
);
|
||||||
|
|
||||||
|
this.testContextManager.beforeTestExecution(this, this.testMethod);
|
||||||
|
assertExecutionOrder("beforeTestExecution",
|
||||||
|
"beforeTestMethod-1",
|
||||||
|
"beforeTestMethod-2",
|
||||||
|
"beforeTestMethod-3",
|
||||||
|
"beforeTestExecution-1",
|
||||||
|
"beforeTestExecution-2",
|
||||||
|
"beforeTestExecution-3"
|
||||||
|
);
|
||||||
|
|
||||||
|
this.testContextManager.afterTestExecution(this, this.testMethod, null);
|
||||||
|
assertExecutionOrder("afterTestExecution",
|
||||||
|
"beforeTestMethod-1",
|
||||||
|
"beforeTestMethod-2",
|
||||||
|
"beforeTestMethod-3",
|
||||||
|
"beforeTestExecution-1",
|
||||||
|
"beforeTestExecution-2",
|
||||||
|
"beforeTestExecution-3",
|
||||||
|
"afterTestExecution-3",
|
||||||
|
"afterTestExecution-2",
|
||||||
|
"afterTestExecution-1"
|
||||||
|
);
|
||||||
|
|
||||||
|
this.testContextManager.afterTestMethod(this, this.testMethod, null);
|
||||||
|
assertExecutionOrder("afterTestMethod",
|
||||||
|
"beforeTestMethod-1",
|
||||||
|
"beforeTestMethod-2",
|
||||||
|
"beforeTestMethod-3",
|
||||||
|
"beforeTestExecution-1",
|
||||||
|
"beforeTestExecution-2",
|
||||||
|
"beforeTestExecution-3",
|
||||||
|
"afterTestExecution-3",
|
||||||
|
"afterTestExecution-2",
|
||||||
|
"afterTestExecution-1",
|
||||||
|
"afterTestMethod-3",
|
||||||
|
"afterTestMethod-2",
|
||||||
|
"afterTestMethod-1"
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
private static void assertExecutionOrder(String usageContext, String... expectedBeforeTestMethodCalls) {
|
||||||
public void tearDownTestContextManager() throws Throwable {
|
assertEquals("execution order (" + usageContext + ") ==>", Arrays.asList(expectedBeforeTestMethodCalls),
|
||||||
this.testContextManager.afterTestMethod(new ExampleTestCase(), getTestMethod(), null);
|
executionOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -140,53 +114,57 @@ public class TestContextManagerTests {
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public void exampleTestMethod() {
|
public void exampleTestMethod() {
|
||||||
assertTrue(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class NamedTestExecutionListener extends AbstractTestExecutionListener {
|
private static class NamedTestExecutionListener implements TestExecutionListener {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
|
|
||||||
public NamedTestExecutionListener(final String name) {
|
public NamedTestExecutionListener(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeTestMethod(final TestContext testContext) {
|
public void beforeTestMethod(TestContext testContext) {
|
||||||
beforeTestMethodCalls.add(this.name);
|
executionOrder.add("beforeTestMethod-" + this.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterTestMethod(final TestContext testContext) {
|
public void beforeTestExecution(TestContext testContext) {
|
||||||
afterTestMethodCalls.add(this.name);
|
executionOrder.add("beforeTestExecution-" + this.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public void afterTestExecution(TestContext testContext) {
|
||||||
return new ToStringCreator(this).append("name", this.name).toString();
|
executionOrder.add("afterTestExecution-" + this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTestMethod(TestContext testContext) {
|
||||||
|
executionOrder.add("afterTestMethod-" + this.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class FirstTel extends NamedTestExecutionListener {
|
private static class FirstTel extends NamedTestExecutionListener {
|
||||||
|
|
||||||
public FirstTel() {
|
public FirstTel() {
|
||||||
super(FIRST);
|
super("1");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SecondTel extends NamedTestExecutionListener {
|
private static class SecondTel extends NamedTestExecutionListener {
|
||||||
|
|
||||||
public SecondTel() {
|
public SecondTel() {
|
||||||
super(SECOND);
|
super("2");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ThirdTel extends NamedTestExecutionListener {
|
private static class ThirdTel extends NamedTestExecutionListener {
|
||||||
|
|
||||||
public ThirdTel() {
|
public ThirdTel() {
|
||||||
super(THIRD);
|
super("3");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue