[SPR-4702] Added support for @DirtiesContext at the test class level.

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@1471 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
Sam Brannen 2009-07-06 12:58:56 +00:00
parent 4ec91c47c3
commit edab801f88
17 changed files with 714 additions and 135 deletions

View File

@ -23,14 +23,30 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Test annotation to indicate that a test method <em>dirties</em> the context
* for the current test.
*
* Test annotation which indicates that the
* {@link org.springframework.context.ApplicationContext ApplicationContext}
* associated with a test is <em>dirty</em> and should be closed:
* <ul>
* <li>after the current test, when declared at the method level, or</li>
* <li>after the current test class, when declared at the class level.</li>
* </ul>
* <p>
* Use this annotation if a test has modified the context (for example, by
* replacing a bean definition). Subsequent tests will be supplied a new
* context.
* </p>
* <p>
* <code>&#064;DirtiesContext</code> may be used as a class-level and
* method-level annotation within the same class. In such scenarios, the
* <code>ApplicationContext</code> will be marked as <em>dirty</em> after any
* such annotated method as well as after the entire class.
* </p>
*
* @author Rod Johnson
* @author Sam Brannen
* @since 2.0
*/
@Target({ElementType.METHOD})
@Target( { ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DirtiesContext {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* 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.
@ -236,6 +236,39 @@ public class TestContextManager {
return defaultListenerClasses;
}
/**
* 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
* <em>before class methods</em> (e.g., methods annotated with JUnit's
* {@link org.junit.BeforeClass &#064;BeforeClass}).
* <p>
* An attempt will be made to give each registered
* {@link TestExecutionListener} a chance to pre-process the test class
* execution. If a listener throws an exception, however, the remaining
* registered listeners will <strong>not</strong> be called.
*
* @throws Exception if a registered TestExecutionListener throws an
* exception
* @see #getTestExecutionListeners()
*/
public void beforeTestClass() throws Exception {
final Class<?> testClass = getTestContext().getTestClass();
if (logger.isTraceEnabled()) {
logger.trace("beforeTestClass(): class [" + testClass + "]");
}
for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {
try {
testExecutionListener.beforeTestClass(getTestContext());
}
catch (Exception ex) {
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener
+ "] to process 'before class' callback for test class [" + testClass + "]", ex);
throw ex;
}
}
}
/**
* Hook for preparing a test instance prior to execution of any individual
* test methods, for example for injecting dependencies, etc. Should be
@ -279,7 +312,7 @@ public class TestContextManager {
* {@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} ).
* 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>.
@ -377,4 +410,51 @@ public class TestContextManager {
}
}
/**
* Hook for post-processing a test class <em>after</em> execution of all
* tests within the class. Should be called after any framework-specific
* <em>after class methods</em> (e.g., methods annotated with JUnit's
* {@link org.junit.AfterClass &#064;AfterClass}).
* <p>
* Each registered {@link TestExecutionListener} will be given a chance to
* post-process the test class. 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.
*
* @throws Exception if a registered TestExecutionListener throws an
* exception
* @see #getTestExecutionListeners()
*/
public void afterTestClass() throws Exception {
final Class<?> testClass = getTestContext().getTestClass();
if (logger.isTraceEnabled()) {
logger.trace("afterTestClass(): class [" + testClass + "]");
}
// Traverse the TestExecutionListeners in reverse order to ensure proper
// "wrapper"-style execution of listeners.
List<TestExecutionListener> listenersReversed = new ArrayList<TestExecutionListener>(
getTestExecutionListeners());
Collections.reverse(listenersReversed);
Exception afterTestClassException = null;
for (TestExecutionListener testExecutionListener : listenersReversed) {
try {
testExecutionListener.afterTestClass(getTestContext());
}
catch (Exception ex) {
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener
+ "] to process 'after class' callback for test class [" + testClass + "]", ex);
if (afterTestClassException == null) {
afterTestClassException = ex;
}
}
}
if (afterTestClassException != null) {
throw afterTestClassException;
}
}
}

View File

@ -31,25 +31,43 @@ package org.springframework.test.context;
* Spring provides the following out-of-the-box implementations:
* </p>
* <ul>
* <li>{@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener DependencyInjectionTestExecutionListener}</li>
* <li>{@link org.springframework.test.context.support.DirtiesContextTestExecutionListener DirtiesContextTestExecutionListener}</li>
* <li>{@link org.springframework.test.context.transaction.TransactionalTestExecutionListener TransactionalTestExecutionListener}</li>
* <li>
* {@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener
* DependencyInjectionTestExecutionListener}</li>
* <li>
* {@link org.springframework.test.context.support.DirtiesContextTestExecutionListener
* DirtiesContextTestExecutionListener}</li>
* <li>
* {@link org.springframework.test.context.transaction.TransactionalTestExecutionListener
* TransactionalTestExecutionListener}</li>
* </ul>
*
*
* @author Sam Brannen
* @author Juergen Hoeller
* @since 2.5
*/
public interface TestExecutionListener {
/**
* Pre-processes a test class <em>before</em> execution of all tests within
* the class.
* <p>
* This method should be called immediately before framework-specific
* <em>before class</em> lifecycle callbacks.
*
* @param testContext the test context for the test; never <code>null</code>
* @throws Exception allows any exception to propagate
*/
void beforeTestClass(TestContext testContext) throws Exception;
/**
* Prepares the {@link Object test instance} of the supplied
* {@link TestContext test context}, for example by injecting
* dependencies.
* <p>This method should be called immediately after instantiation
* of the test instance but prior to any framework-specific lifecycle
* callbacks.
* @param testContext the test context for the test (never <code>null</code>)
* {@link TestContext test context}, for example by injecting dependencies.
* <p>
* This method should be called immediately after instantiation of the test
* instance but prior to any framework-specific lifecycle callbacks.
*
* @param testContext the test context for the test; never <code>null</code>
* @throws Exception allows any exception to propagate
*/
void prepareTestInstance(TestContext testContext) throws Exception;
@ -59,10 +77,12 @@ public interface TestExecutionListener {
* {@link java.lang.reflect.Method test method} in the supplied
* {@link TestContext test context}, for example by setting up test
* fixtures.
* <p>This method should be called immediately prior to any
* framework-specific <em>before</em> lifecycle callbacks.
* <p>
* This method should be called immediately prior to framework-specific
* <em>before</em> lifecycle callbacks.
*
* @param testContext the test context in which the test method will be
* executed (never <code>null</code>)
* executed; never <code>null</code>
* @throws Exception allows any exception to propagate
*/
void beforeTestMethod(TestContext testContext) throws Exception;
@ -72,12 +92,26 @@ public interface TestExecutionListener {
* {@link java.lang.reflect.Method test method} in the supplied
* {@link TestContext test context}, for example by tearing down test
* fixtures.
* <p>This method should be called immediately after any
* framework-specific <em>after</em> lifecycle callbacks.
* <p>
* This method should be called immediately after framework-specific
* <em>after</em> lifecycle callbacks.
*
* @param testContext the test context in which the test method was
* executed (never <code>null</code>)
* executed; never <code>null</code>
* @throws Exception allows any exception to propagate
*/
void afterTestMethod(TestContext testContext) throws Exception;
/**
* Post-processes a test class <em>after</em> execution of all tests within
* the class.
* <p>
* This method should be called immediately after framework-specific
* <em>after class</em> lifecycle callbacks.
*
* @param testContext the test context for the test; never <code>null</code>
* @throws Exception allows any exception to propagate
*/
void afterTestClass(TestContext testContext) throws Exception;
}

View File

@ -39,8 +39,10 @@ import org.springframework.test.annotation.ProfileValueUtils;
import org.springframework.test.annotation.Repeat;
import org.springframework.test.annotation.Timed;
import org.springframework.test.context.TestContextManager;
import org.springframework.test.context.junit4.statements.RunSpringTestContextAfters;
import org.springframework.test.context.junit4.statements.RunSpringTestContextBefores;
import org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks;
import org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks;
import org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks;
import org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks;
import org.springframework.test.context.junit4.statements.SpringFailOnTimeout;
import org.springframework.test.context.junit4.statements.SpringRepeat;
@ -142,20 +144,6 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner {
return null;
}
/**
* Delegates to the parent implementation for creating the test instance and
* then allows the {@link #getTestContextManager() TestContextManager} to
* prepare the test instance before returning it.
*
* @see TestContextManager#prepareTestInstance(Object)
*/
@Override
protected Object createTest() throws Exception {
Object testInstance = super.createTest();
getTestContextManager().prepareTestInstance(testInstance);
return testInstance;
}
/**
* Returns a description suitable for an ignored test class if the test is
* disabled via <code>&#064;IfProfileValue</code> at the class-level, and
@ -191,6 +179,47 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner {
super.run(notifier);
}
/**
* Wraps the {@link Statement} returned by the parent implementation with a
* {@link RunBeforeTestClassCallbacks} statement, thus preserving the
* default functionality but adding support for the Spring TestContext
* Framework.
*
* @see RunBeforeTestClassCallbacks
*/
@Override
protected Statement withBeforeClasses(Statement statement) {
Statement junitBeforeClasses = super.withBeforeClasses(statement);
return new RunBeforeTestClassCallbacks(junitBeforeClasses, getTestContextManager());
}
/**
* Wraps the {@link Statement} returned by the parent implementation with a
* {@link RunAfterTestClassCallbacks} statement, thus preserving the default
* functionality but adding support for the Spring TestContext Framework.
*
* @see RunAfterTestClassCallbacks
*/
@Override
protected Statement withAfterClasses(Statement statement) {
Statement junitAfterClasses = super.withAfterClasses(statement);
return new RunAfterTestClassCallbacks(junitAfterClasses, getTestContextManager());
}
/**
* Delegates to the parent implementation for creating the test instance and
* then allows the {@link #getTestContextManager() TestContextManager} to
* prepare the test instance before returning it.
*
* @see TestContextManager#prepareTestInstance(Object)
*/
@Override
protected Object createTest() throws Exception {
Object testInstance = super.createTest();
getTestContextManager().prepareTestInstance(testInstance);
return testInstance;
}
/**
* Performs the same logic as
* {@link BlockJUnit4ClassRunner#runChild(FrameworkMethod, RunNotifier)},
@ -393,30 +422,31 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner {
/**
* Wraps the {@link Statement} returned by the parent implementation with a
* {@link RunSpringTestContextBefores} statement, thus preserving the
* {@link RunBeforeTestMethodCallbacks} statement, thus preserving the
* default functionality but adding support for the Spring TestContext
* Framework.
*
* @see RunSpringTestContextBefores
* @see RunBeforeTestMethodCallbacks
*/
@Override
protected Statement withBefores(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) {
Statement junitBefores = super.withBefores(frameworkMethod, testInstance, statement);
return new RunSpringTestContextBefores(junitBefores, testInstance, frameworkMethod.getMethod(),
return new RunBeforeTestMethodCallbacks(junitBefores, testInstance, frameworkMethod.getMethod(),
getTestContextManager());
}
/**
* Wraps the {@link Statement} returned by the parent implementation with a
* {@link RunSpringTestContextAfters} statement, thus preserving the default
* functionality but adding support for the Spring TestContext Framework.
* {@link RunAfterTestMethodCallbacks} statement, thus preserving the
* default functionality but adding support for the Spring TestContext
* Framework.
*
* @see RunSpringTestContextAfters
* @see RunAfterTestMethodCallbacks
*/
@Override
protected Statement withAfters(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) {
Statement junitAfters = super.withAfters(frameworkMethod, testInstance, statement);
return new RunSpringTestContextAfters(junitAfters, testInstance, frameworkMethod.getMethod(),
return new RunAfterTestMethodCallbacks(junitAfters, testInstance, frameworkMethod.getMethod(),
getTestContextManager());
}

View File

@ -0,0 +1,91 @@
/*
* 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.statements;
import java.util.ArrayList;
import java.util.List;
import org.junit.internal.runners.model.MultipleFailureException;
import org.junit.runners.model.Statement;
import org.springframework.test.context.TestContextManager;
/**
* <code>RunAfterTestClassCallbacks</code> is a custom JUnit 4.5+
* {@link Statement} which allows the <em>Spring TestContext Framework</em> to
* be plugged into the JUnit execution chain by calling
* {@link TestContextManager#afterTestClass() afterTestClass()} on the supplied
* {@link TestContextManager}.
*
* @see #evaluate()
* @see RunBeforeTestMethodCallbacks
* @author Sam Brannen
* @since 3.0
*/
public class RunAfterTestClassCallbacks extends Statement {
private final Statement next;
private final TestContextManager testContextManager;
/**
* Constructs a new <code>RunAfterTestClassCallbacks</code> statement.
*
* @param next the next <code>Statement</code> in the execution chain
* @param testContextManager the TestContextManager upon which to call
* <code>afterTestClass()</code>
*/
public RunAfterTestClassCallbacks(Statement next, TestContextManager testContextManager) {
this.next = next;
this.testContextManager = testContextManager;
}
/**
* Invokes the next {@link Statement} in the execution chain (typically an
* instance of {@link org.junit.internal.runners.statements.RunAfters
* RunAfters}), catching any exceptions thrown, and then calls
* {@link TestContextManager#afterTestClass()}. If the call to
* <code>afterTestClass()</code> throws an exception, it will also be
* tracked. Multiple exceptions will be combined into a
* {@link MultipleFailureException}.
*/
@Override
public void evaluate() throws Throwable {
List<Throwable> errors = new ArrayList<Throwable>();
try {
this.next.evaluate();
}
catch (Throwable e) {
errors.add(e);
}
try {
this.testContextManager.afterTestClass();
}
catch (Exception e) {
errors.add(e);
}
if (errors.isEmpty()) {
return;
}
if (errors.size() == 1) {
throw errors.get(0);
}
throw new MultipleFailureException(errors);
}
}

View File

@ -25,18 +25,18 @@ import org.junit.runners.model.Statement;
import org.springframework.test.context.TestContextManager;
/**
* <code>RunSpringTestContextAfters</code> is a custom JUnit 4.5+
* <code>RunAfterTestMethodCallbacks</code> is a custom JUnit 4.5+
* {@link Statement} which allows the <em>Spring TestContext Framework</em> to
* be plugged into the JUnit execution chain by calling
* {@link TestContextManager#afterTestMethod(Object, Method) afterTestMethod()}
* on the supplied {@link TestContextManager}.
*
* @see #evaluate()
* @see RunSpringTestContextBefores
* @see RunBeforeTestMethodCallbacks
* @author Sam Brannen
* @since 3.0
*/
public class RunSpringTestContextAfters extends Statement {
public class RunAfterTestMethodCallbacks extends Statement {
private final Statement next;
@ -48,7 +48,7 @@ public class RunSpringTestContextAfters extends Statement {
/**
* Constructs a new <code>RunSpringTestContextAfters</code> statement.
* Constructs a new <code>RunAfterTestMethodCallbacks</code> statement.
*
* @param next the next <code>Statement</code> in the execution chain
* @param testInstance the current test instance (never <code>null</code>)
@ -57,7 +57,7 @@ public class RunSpringTestContextAfters extends Statement {
* @param testContextManager the TestContextManager upon which to call
* <code>afterTestMethod()</code>
*/
public RunSpringTestContextAfters(Statement next, Object testInstance, Method testMethod,
public RunAfterTestMethodCallbacks(Statement next, Object testInstance, Method testMethod,
TestContextManager testContextManager) {
this.next = next;
this.testInstance = testInstance;

View File

@ -0,0 +1,64 @@
/*
* 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.statements;
import org.junit.runners.model.Statement;
import org.springframework.test.context.TestContextManager;
/**
* <code>RunBeforeTestClassCallbacks</code> is a custom JUnit 4.5+
* {@link Statement} which allows the <em>Spring TestContext Framework</em> to
* be plugged into the JUnit execution chain by calling
* {@link TestContextManager#beforeTestClass() beforeTestClass()} on the
* supplied {@link TestContextManager}.
*
* @see #evaluate()
* @see RunAfterTestMethodCallbacks
* @author Sam Brannen
* @since 3.0
*/
public class RunBeforeTestClassCallbacks extends Statement {
private final Statement next;
private final TestContextManager testContextManager;
/**
* Constructs a new <code>RunBeforeTestClassCallbacks</code> statement.
*
* @param next the next <code>Statement</code> in the execution chain
* @param testContextManager the TestContextManager upon which to call
* <code>beforeTestClass()</code>
*/
public RunBeforeTestClassCallbacks(Statement next, TestContextManager testContextManager) {
this.next = next;
this.testContextManager = testContextManager;
}
/**
* Calls {@link TestContextManager#beforeTestClass()} and then invokes the
* next {@link Statement} in the execution chain (typically an instance of
* {@link org.junit.internal.runners.statements.RunBefores RunBefores}).
*/
@Override
public void evaluate() throws Throwable {
this.testContextManager.beforeTestClass();
this.next.evaluate();
}
}

View File

@ -22,18 +22,18 @@ import org.junit.runners.model.Statement;
import org.springframework.test.context.TestContextManager;
/**
* <code>RunSpringTestContextBefores</code> is a custom JUnit 4.5+
* <code>RunBeforeTestMethodCallbacks</code> is a custom JUnit 4.5+
* {@link Statement} which allows the <em>Spring TestContext Framework</em> to
* be plugged into the JUnit execution chain by calling
* {@link TestContextManager#beforeTestMethod(Object, Method)
* beforeTestMethod()} on the supplied {@link TestContextManager}.
*
* @see #evaluate()
* @see RunSpringTestContextAfters
* @see RunAfterTestMethodCallbacks
* @author Sam Brannen
* @since 3.0
*/
public class RunSpringTestContextBefores extends Statement {
public class RunBeforeTestMethodCallbacks extends Statement {
private final Statement next;
@ -45,7 +45,7 @@ public class RunSpringTestContextBefores extends Statement {
/**
* Constructs a new <code>RunSpringTestContextBefores</code> statement.
* Constructs a new <code>RunBeforeTestMethodCallbacks</code> statement.
*
* @param next the next <code>Statement</code> in the execution chain
* @param testInstance the current test instance (never <code>null</code>)
@ -54,7 +54,7 @@ public class RunSpringTestContextBefores extends Statement {
* @param testContextManager the TestContextManager upon which to call
* <code>beforeTestMethod()</code>
*/
public RunSpringTestContextBefores(Statement next, Object testInstance, Method testMethod,
public RunBeforeTestMethodCallbacks(Statement next, Object testInstance, Method testMethod,
TestContextManager testContextManager) {
this.next = next;
this.testInstance = testInstance;
@ -66,7 +66,7 @@ public class RunSpringTestContextBefores extends Statement {
* Calls {@link TestContextManager#beforeTestMethod(Object, Method)} and
* then invokes the next {@link Statement} in the execution chain (typically
* an instance of {@link org.junit.internal.runners.statements.RunBefores
* RunBefores} ).
* RunBefores}).
*/
@Override
public void evaluate() throws Throwable {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* 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.
@ -23,13 +23,21 @@ import org.springframework.test.context.TestExecutionListener;
* Abstract implementation of the {@link TestExecutionListener} interface which
* provides empty method stubs. Subclasses can extend this class and override
* only those methods suitable for the task at hand.
*
*
* @author Sam Brannen
* @author Juergen Hoeller
* @since 2.5
*/
public abstract class AbstractTestExecutionListener implements TestExecutionListener {
/**
* The default implementation is <em>empty</em>. Can be overridden by
* subclasses as necessary.
*/
public void beforeTestClass(TestContext testContext) throws Exception {
/* no-op */
}
/**
* The default implementation is <em>empty</em>. Can be overridden by
* subclasses as necessary.
@ -54,4 +62,12 @@ public abstract class AbstractTestExecutionListener implements TestExecutionList
/* no-op */
}
/**
* The default implementation is <em>empty</em>. Can be overridden by
* subclasses as necessary.
*/
public void afterTestClass(TestContext testContext) throws Exception {
/* no-op */
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* 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.
@ -20,16 +20,16 @@ import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestContext;
import org.springframework.util.Assert;
/**
* <code>TestExecutionListener</code> which processes test methods configured
* with the {@link DirtiesContext @DirtiesContext} annotation.
*
* <code>TestExecutionListener</code> which processes test classes and test
* methods configured with the {@link DirtiesContext &#064;DirtiesContext}
* annotation.
*
* @author Sam Brannen
* @author Juergen Hoeller
* @since 2.5
@ -41,14 +41,27 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi
/**
* If the current test method of the supplied
* {@link TestContext test context} has been annotated with
* {@link DirtiesContext @DirtiesContext}, the
* {@link ApplicationContext application context} of the test context will
* be {@link TestContext#markApplicationContextDirty() marked as dirty},
* and the
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE}
* will be set to <code>true</code> in the test context.
* Marks the {@link ApplicationContext application context} of the supplied
* {@link TestContext test context} as
* {@link TestContext#markApplicationContextDirty() dirty}, and sets the
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE
* REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context to <code>true</code>
* .
*/
protected void dirtyContext(TestContext testContext) {
testContext.markApplicationContextDirty();
testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE, Boolean.TRUE);
}
/**
* If the current test method of the supplied {@link TestContext test
* context} is annotated with {@link DirtiesContext &#064;DirtiesContext},
* the {@link ApplicationContext application context} of the test context
* will be {@link TestContext#markApplicationContextDirty() marked as dirty}
* , and the
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE
* REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context will be set to
* <code>true</code>.
*/
@Override
public void afterTestMethod(TestContext testContext) throws Exception {
@ -59,11 +72,32 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi
if (logger.isDebugEnabled()) {
logger.debug("After test method: context [" + testContext + "], dirtiesContext [" + dirtiesContext + "].");
}
if (dirtiesContext) {
testContext.markApplicationContextDirty();
testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE,
Boolean.TRUE);
dirtyContext(testContext);
}
}
/**
* If the test class of the supplied {@link TestContext test context} is
* annotated with {@link DirtiesContext &#064;DirtiesContext}, the
* {@link ApplicationContext application context} of the test context will
* be {@link TestContext#markApplicationContextDirty() marked as dirty} ,
* and the
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE
* REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context will be set to
* <code>true</code>.
*/
@Override
public void afterTestClass(TestContext testContext) throws Exception {
Class<?> testClass = testContext.getTestClass();
Assert.notNull(testClass, "The test class of the supplied TestContext must not be null");
boolean dirtiesContext = testClass.isAnnotationPresent(DirtiesContext.class);
if (logger.isDebugEnabled()) {
logger.debug("After test class: context [" + testContext + "], dirtiesContext [" + dirtiesContext + "].");
}
if (dirtiesContext) {
dirtyContext(testContext);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* 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.
@ -16,17 +16,11 @@
package org.springframework.test.context.testng;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.testng.IHookCallBack;
import org.testng.IHookable;
import org.testng.ITestResult;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.test.context.ContextConfiguration;
@ -35,6 +29,13 @@ import org.springframework.test.context.TestContextManager;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
import org.testng.IHookCallBack;
import org.testng.IHookable;
import org.testng.ITestResult;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
/**
* <p>
@ -47,19 +48,19 @@ import org.springframework.test.context.support.DirtiesContextTestExecutionListe
* Concrete subclasses:
* </p>
* <ul>
* <li>Typically declare a class-level
* {@link ContextConfiguration @ContextConfiguration} annotation to configure
* the {@link ApplicationContext application context}
* <li>Typically declare a class-level {@link ContextConfiguration
* &#064;ContextConfiguration} annotation to configure the
* {@link ApplicationContext application context}
* {@link ContextConfiguration#locations() resource locations}.
* <em>If your test does not need to load an application context, you may choose
* to omit the {@link ContextConfiguration @ContextConfiguration} declaration
* to omit the {@link ContextConfiguration &#064;ContextConfiguration} declaration
* and to configure the appropriate
* {@link org.springframework.test.context.TestExecutionListener TestExecutionListeners}
* manually.</em></li>
* <li>Must have constructors which either implicitly or explicitly delegate to
* <code>super();</code>.</li>
* </ul>
*
*
* @author Sam Brannen
* @author Juergen Hoeller
* @since 2.5
@ -70,7 +71,7 @@ import org.springframework.test.context.support.DirtiesContextTestExecutionListe
* @see org.springframework.test.context.junit38.AbstractJUnit38SpringContextTests
* @see org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests
*/
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class})
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class })
public abstract class AbstractTestNGSpringContextTests implements IHookable, ApplicationContextAware {
/** Logger available to subclasses */
@ -88,8 +89,8 @@ public abstract class AbstractTestNGSpringContextTests implements IHookable, App
/**
* Construct a new AbstractTestNGSpringContextTests instance and
* initializes the internal {@link TestContextManager} for the current test.
* Construct a new AbstractTestNGSpringContextTests instance and initialize
* the internal {@link TestContextManager} for the current test.
*/
public AbstractTestNGSpringContextTests() {
this.testContextManager = new TestContextManager(getClass());
@ -98,21 +99,36 @@ public abstract class AbstractTestNGSpringContextTests implements IHookable, App
/**
* Set the {@link ApplicationContext} to be used by this test instance,
* provided via {@link ApplicationContextAware} semantics.
*
* @param applicationContext the applicationContext to set
*/
public final void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* Delegates to the configured {@link TestContextManager} to call
* {@link TestContextManager#beforeTestClass() 'before test class'}
* callbacks.
*
* @throws Exception if a registered TestExecutionListener throws an
* exception
*/
@BeforeClass(alwaysRun = true)
protected void springTestContextBeforeTestClass() throws Exception {
this.testContextManager.beforeTestClass();
}
/**
* Delegates to the configured {@link TestContextManager} to
* {@link TestContextManager#prepareTestInstance(Object) prepare} this test
* instance prior to execution of any individual tests, for example for
* injecting dependencies, etc.
* @throws Exception if a registered TestExecutionListener throws an exception
*
* @throws Exception if a registered TestExecutionListener throws an
* exception
*/
@BeforeClass(alwaysRun = true)
@BeforeClass(alwaysRun = true, dependsOnMethods = "springTestContextBeforeTestClass")
protected void springTestContextPrepareTestInstance() throws Exception {
this.testContextManager.prepareTestInstance(this);
}
@ -121,6 +137,7 @@ public abstract class AbstractTestNGSpringContextTests implements IHookable, App
* Delegates to the configured {@link TestContextManager} to
* {@link TestContextManager#beforeTestMethod(Object,Method) pre-process}
* the test method before the actual test is executed.
*
* @param testMethod the test method which is about to be executed.
* @throws Exception allows all exceptions to propagate.
*/
@ -130,22 +147,30 @@ public abstract class AbstractTestNGSpringContextTests implements IHookable, App
}
/**
* Delegates to the
* {@link IHookCallBack#runTestMethod(ITestResult) test method} in the
* supplied <code>callback</code> to execute the actual test and then
* tracks the exception thrown during test execution, if any.
* @see org.testng.IHookable#run(org.testng.IHookCallBack, org.testng.ITestResult)
* Delegates to the {@link IHookCallBack#runTestMethod(ITestResult) test
* method} in the supplied <code>callback</code> to execute the actual test
* and then tracks the exception thrown during test execution, if any.
*
* @see org.testng.IHookable#run(org.testng.IHookCallBack,
* org.testng.ITestResult)
*/
public void run(IHookCallBack callBack, ITestResult testResult) {
callBack.runTestMethod(testResult);
this.testException = testResult.getThrowable();
Throwable testResultException = testResult.getThrowable();
if (testResultException instanceof InvocationTargetException) {
testResultException = ((InvocationTargetException) testResultException).getCause();
}
this.testException = testResultException;
}
/**
* Delegates to the configured {@link TestContextManager} to
* {@link TestContextManager#afterTestMethod(Object, Method, Throwable) post-process}
* the test method after the actual test has executed.
* @param testMethod the test method which has just been executed on the test instance
* {@link TestContextManager#afterTestMethod(Object, Method, Throwable)
* post-process} the test method after the actual test has executed.
*
* @param testMethod the test method which has just been executed on the
* test instance
* @throws Exception allows all exceptions to propagate
*/
@AfterMethod(alwaysRun = true)
@ -158,4 +183,16 @@ public abstract class AbstractTestNGSpringContextTests implements IHookable, App
}
}
/**
* Delegates to the configured {@link TestContextManager} to call
* {@link TestContextManager#afterTestClass() 'after test class'} callbacks.
*
* @throws Exception if a registered TestExecutionListener throws an
* exception
*/
@AfterClass(alwaysRun = true)
protected void springTestContextAfterTestClass() throws Exception {
this.testContextManager.afterTestClass();
}
}

View File

@ -0,0 +1,174 @@
/*
* 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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.JUnitCore;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.TrackingRunListener;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
/**
* JUnit 4 based integration test which verifies correct {@link ContextCache
* application context caching} in conjunction with the
* {@link SpringJUnit4ClassRunner} and the {@link DirtiesContext
* &#064;DirtiesContext} annotation at the class level.
*
* @author Sam Brannen
* @since 3.0
*/
@RunWith(JUnit4.class)
public class ClassLevelDirtiesContextTests {
/**
* Asserts the statistics of the supplied context cache.
*
* @param usageScenario the scenario in which the statistics are used
* @param expectedSize the expected number of contexts in the cache
* @param expectedHitCount the expected hit count
* @param expectedMissCount the expected miss count
*/
private static final void assertContextCacheStatistics(String usageScenario, int expectedSize,
int expectedHitCount, int expectedMissCount) {
ContextCache contextCache = TestContextManager.contextCache;
assertEquals("Verifying number of contexts in cache (" + usageScenario + ").", expectedSize,
contextCache.size());
assertEquals("Verifying number of cache hits (" + usageScenario + ").", expectedHitCount,
contextCache.getHitCount());
assertEquals("Verifying number of cache misses (" + usageScenario + ").", expectedMissCount,
contextCache.getMissCount());
}
private static final void runTestClassAndAssertRunListenerStats(Class<?> testClass) {
final int expectedTestFailureCount = 0;
final int expectedTestStartedCount = 1;
final int expectedTestFinishedCount = 1;
TrackingRunListener listener = new TrackingRunListener();
JUnitCore jUnitCore = new JUnitCore();
jUnitCore.addListener(listener);
jUnitCore.run(testClass);
assertEquals("Verifying number of failures for test class [" + testClass + "].", expectedTestFailureCount,
listener.getTestFailureCount());
assertEquals("Verifying number of tests started for test class [" + testClass + "].", expectedTestStartedCount,
listener.getTestStartedCount());
assertEquals("Verifying number of tests finished for test class [" + testClass + "].",
expectedTestFinishedCount, listener.getTestFinishedCount());
}
@BeforeClass
public static void verifyInitialCacheState() {
ContextCache contextCache = TestContextManager.contextCache;
contextCache.clear();
contextCache.clearStatistics();
assertContextCacheStatistics("BeforeClass", 0, 0, 0);
}
@AfterClass
public static void verifyFinalCacheState() {
assertContextCacheStatistics("AfterClass", 0, 3, 5);
}
@Test
public void verifyDirtiesContextBehavior() throws Exception {
int hits = 0;
int misses = 0;
runTestClassAndAssertRunListenerStats(CleanTestCase.class);
assertContextCacheStatistics("after clean test class", 1, hits, ++misses);
runTestClassAndAssertRunListenerStats(ClassLevelDirtiesContextWithCleanMethodsTestCase.class);
assertContextCacheStatistics("after class-level @DirtiesContext with clean test method", 0, ++hits, misses);
runTestClassAndAssertRunListenerStats(CleanTestCase.class);
assertContextCacheStatistics("after clean test class", 1, hits, ++misses);
runTestClassAndAssertRunListenerStats(ClassLevelDirtiesContextWithDirtyMethodsTestCase.class);
assertContextCacheStatistics("after class-level @DirtiesContext with dirty test method", 0, ++hits, misses);
runTestClassAndAssertRunListenerStats(ClassLevelDirtiesContextWithDirtyMethodsTestCase.class);
assertContextCacheStatistics("after class-level @DirtiesContext with dirty test method", 0, hits, ++misses);
runTestClassAndAssertRunListenerStats(ClassLevelDirtiesContextWithDirtyMethodsTestCase.class);
assertContextCacheStatistics("after class-level @DirtiesContext with dirty test method", 0, hits, ++misses);
runTestClassAndAssertRunListenerStats(CleanTestCase.class);
assertContextCacheStatistics("after clean test class", 1, hits, ++misses);
runTestClassAndAssertRunListenerStats(ClassLevelDirtiesContextWithCleanMethodsTestCase.class);
assertContextCacheStatistics("after class-level @DirtiesContext with clean test method", 0, ++hits, misses);
}
@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class })
@ContextConfiguration("/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerAppCtxTests-context.xml")
public static abstract class BaseTestCase {
@Autowired
protected ApplicationContext applicationContext;
protected void assertApplicationContextWasAutowired() {
assertNotNull("The application context should have been autowired.", this.applicationContext);
}
}
public static final class CleanTestCase extends BaseTestCase {
@Test
public void verifyContextWasAutowired() {
assertApplicationContextWasAutowired();
}
}
@DirtiesContext
public static final class ClassLevelDirtiesContextWithCleanMethodsTestCase extends BaseTestCase {
@Test
public void verifyContextWasAutowired() {
assertApplicationContextWasAutowired();
}
}
@DirtiesContext
public static final class ClassLevelDirtiesContextWithDirtyMethodsTestCase extends BaseTestCase {
@Test
@DirtiesContext
public void dirtyContext() {
assertApplicationContextWasAutowired();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* 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.
@ -25,47 +25,49 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* JUnit 4 based unit test which verifies correct
* {@link ContextCache application context caching} in conjunction with the
* {@link SpringJUnit4ClassRunner} and the {@link DirtiesContext} annotation.
*
* JUnit 4 based unit test which verifies correct {@link ContextCache
* application context caching} in conjunction with the
* {@link SpringJUnit4ClassRunner} and the {@link DirtiesContext
* &#064;DirtiesContext} annotation at the method level.
*
* @author Sam Brannen
* @author Juergen Hoeller
* @since 2.5
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerAppCtxTests-context.xml")
public class SpringRunnerContextCacheTests implements ApplicationContextAware {
@ContextConfiguration("/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerAppCtxTests-context.xml")
public class SpringRunnerContextCacheTests {
private static ApplicationContext dirtiedApplicationContext;
@Autowired
protected ApplicationContext applicationContext;
/**
* Asserts the statistics of the supplied context cache.
*
* @param usageScenario the scenario in which the statistics are used
* @param expectedSize the expected number of contexts in the cache
* @param expectedHitCount the expected hit count
* @param expectedMissCount the expected miss count
*/
public static final void assertContextCacheStatistics(String usageScenario, int expectedSize,
int expectedHitCount, int expectedMissCount) {
public static final void assertContextCacheStatistics(String usageScenario, int expectedSize, int expectedHitCount,
int expectedMissCount) {
ContextCache contextCache = TestContextManager.contextCache;
assertEquals("Verifying number of contexts in cache (" + usageScenario + ").", expectedSize,
contextCache.size());
contextCache.size());
assertEquals("Verifying number of cache hits (" + usageScenario + ").", expectedHitCount,
contextCache.getHitCount());
contextCache.getHitCount());
assertEquals("Verifying number of cache misses (" + usageScenario + ").", expectedMissCount,
contextCache.getMissCount());
contextCache.getMissCount());
}
@BeforeClass
@ -82,37 +84,29 @@ public class SpringRunnerContextCacheTests implements ApplicationContextAware {
assertContextCacheStatistics("AfterClass", 1, 1, 2);
}
public final void setApplicationContext(final ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Test
@DirtiesContext
public void dirtyContext() {
assertContextCacheStatistics("dirtyContext()", 1, 0, 1);
assertNotNull("The application context should have been set due to ApplicationContextAware semantics.",
this.applicationContext);
assertNotNull("The application context should have been autowired.", this.applicationContext);
SpringRunnerContextCacheTests.dirtiedApplicationContext = this.applicationContext;
}
@Test
public void verifyContextWasDirtied() {
assertContextCacheStatistics("verifyContextWasDirtied()", 1, 0, 2);
assertNotNull("The application context should have been set due to ApplicationContextAware semantics.",
this.applicationContext);
assertNotNull("The application context should have been autowired.", this.applicationContext);
assertNotSame("The application context should have been 'dirtied'.",
SpringRunnerContextCacheTests.dirtiedApplicationContext, this.applicationContext);
SpringRunnerContextCacheTests.dirtiedApplicationContext, this.applicationContext);
SpringRunnerContextCacheTests.dirtiedApplicationContext = this.applicationContext;
}
@Test
public void verifyContextWasNotDirtied() {
assertContextCacheStatistics("verifyContextWasNotDirtied()", 1, 1, 2);
assertNotNull("The application context should have been set due to ApplicationContextAware semantics.",
this.applicationContext);
assertNotNull("The application context should have been autowired.", this.applicationContext);
assertSame("The application context should NOT have been 'dirtied'.",
SpringRunnerContextCacheTests.dirtiedApplicationContext, this.applicationContext);
SpringRunnerContextCacheTests.dirtiedApplicationContext, this.applicationContext);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* 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.
@ -17,9 +17,9 @@
package org.springframework.test.context.junit4;
import static org.junit.Assert.fail;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.IfProfileValue;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
@ -27,6 +27,7 @@ import org.springframework.test.context.TestExecutionListeners;
/**
* @author Juergen Hoeller
* @author Sam Brannen
*/
@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners(ClassLevelDisabledSpringRunnerTests.CustomTestExecutionListener.class)
@ -41,6 +42,10 @@ public class ClassLevelDisabledSpringRunnerTests {
public static class CustomTestExecutionListener implements TestExecutionListener {
public void beforeTestClass(TestContext testContext) throws Exception {
fail("A listener method for a disabled test should never be executed!");
}
public void prepareTestInstance(TestContext testContext) throws Exception {
fail("A listener method for a disabled test should never be executed!");
}
@ -52,6 +57,9 @@ public class ClassLevelDisabledSpringRunnerTests {
public void afterTestMethod(TestContext testContext) throws Exception {
fail("A listener method for a disabled test should never be executed!");
}
}
public void afterTestClass(TestContext testContext) throws Exception {
fail("A listener method for a disabled test should never be executed!");
}
}
}

View File

@ -19,6 +19,7 @@ package org.springframework.test.context.junit4;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import org.springframework.test.context.ClassLevelDirtiesContextTests;
import org.springframework.test.context.SpringRunnerContextCacheTests;
/**
@ -59,6 +60,7 @@ StandardJUnit4FeaturesTests.class,//
PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests.class,//
CustomDefaultContextLoaderClassSpringRunnerTests.class,//
SpringRunnerContextCacheTests.class,//
ClassLevelDirtiesContextTests.class,//
ParameterizedDependencyInjectionTests.class,//
ClassLevelTransactionalSpringRunnerTests.class,//
MethodLevelTransactionalSpringRunnerTests.class,//

View File

@ -51,7 +51,6 @@ import org.testng.annotations.Test;
* @author Sam Brannen
* @since 2.5
*/
@org.junit.Ignore("TestNG tests should not be run by JUnit")
@ContextConfiguration
public class ConcreteTransactionalTestNGSpringContextTests extends AbstractTransactionalTestNGSpringContextTests
implements BeanNameAware, InitializingBean {

View File

@ -32,7 +32,7 @@ import org.testng.annotations.Test;
* TestNG based unit test to assess the claim in <a
* href="http://opensource.atlassian.com/projects/spring/browse/SPR-3880"
* target="_blank">SPR-3880</a> that a &quot;context marked dirty using
* {@link DirtiesContext @DirtiesContext} in [a] TestNG based test is not
* {@link DirtiesContext &#064;DirtiesContext} in [a] TestNG based test is not
* reloaded in subsequent tests&quot;.
* </p>
* <p>
@ -47,7 +47,6 @@ import org.testng.annotations.Test;
* @author Sam Brannen
* @since 2.5
*/
@org.junit.Ignore("TestNG tests should not be run by JUnit")
@ContextConfiguration
public class DirtiesContextTransactionalTestNGSpringContextTests extends AbstractTransactionalTestNGSpringContextTests {
@ -82,4 +81,5 @@ public class DirtiesContextTransactionalTestNGSpringContextTests extends Abstrac
assertSame(this.applicationContext, this.dirtiedApplicationContext,
"The application context should NOT have been 'dirtied'.");
}
}