Closed
Description
Jason Zaugg opened SPR-4638 and commented
TransactionalTestExecutionListener:378
protected final PlatformTransactionManager getTransactionManager(final TestContext testContext) throws Exception {
if (this.transactionManager != null) {
return this.transactionManager;
}
// else...
final TransactionConfigurationAttributes configAttributes = retrieveTransactionConfigurationAttributes(testContext.getTestClass());
final String transactionManagerName = configAttributes.getTransactionManagerName();
try {
this.transactionManager = (PlatformTransactionManager) testContext.getApplicationContext().getBean(
transactionManagerName, PlatformTransactionManager.class);
}
I suggest that you do the following instead:
if (this.transactionManager != null && this.applicationContext = testContext.getApplicationContext()) {
return this.transactionManager;
}
// else...
final TransactionConfigurationAttributes configAttributes = retrieveTransactionConfigurationAttributes(testContext.getTestClass());
final String transactionManagerName = configAttributes.getTransactionManagerName();
try {
this.transactionManager = (PlatformTransactionManager) testContext.getApplicationContext().getBean(
transactionManagerName, PlatformTransactionManager.class);
this.applicationContext = testContext.getApplicationContext();
}
I experience this when I am using a custom TestRunner and TestContextManager, who are responsible for injecting Mock objects into the ApplicationWiring if the Test Case contains suitably annotated fields. As a workaround, my custom TestContextManager reflectively sets the TransactionalTestExecutionListener.transactionManager to null after each test.
public final class RaptorTestRunner extends SpringJUnit4ClassRunner {
public RaptorTestRunner(Class testClass) throws InitializationError {
super(testClass);
}
protected TestContextManager createTestContextManager(Class<?> clazz) {
TestContextManager contextManager = new TestContextManager(clazz) {
public TestInstancePreparer preparer;
public void prepareTestInstance(Object testInstance) throws Exception {
TestContext testContext = getTestContext();
ApplicationContext springApplicationContext = testContext.getApplicationContext();
Map map = springApplicationContext.getBeansOfType(Context.class);
Context raptorContext = (Context) toArray(map.values().iterator())[0];
preparer = new TestInstancePreparer(testInstance, testContext, raptorContext);
preparer.prepareSubject();
}
public void afterTestMethod(Object testInstance, Method testMethod, Throwable exception) throws Exception {
super.afterTestMethod(testInstance, testMethod, exception);
preparer.discardApplicationContextIfInjectWasUsed();
forceSpringTransactionTestListenerToUseTheLatestDatasource();
}
private void forceSpringTransactionTestListenerToUseTheLatestDatasource() throws NoSuchFieldException, IllegalAccessException {
List<TestExecutionListener> listeners = getTestExecutionListeners();
for (TestExecutionListener listener : listeners) {
// TODO Raise line 378 of TransactionalTestExecutionListener returns a transaction
// manager that can point to a datasource that has been closed, if the spring application context
// for the first test has been marked as dirty and closed.
if (listener instanceof TransactionalTestExecutionListener) {
Field field = listener.getClass().getDeclaredField("transactionManager");
field.setAccessible(true);
field.set(listener, null);
}
}
}
};
return contextManager;
}
}
Affects: 2.5.1
Issue Links:
- Support programmatic starting and stopping of transactions in the TestContext framework [SPR-5079] #9753 Support programmatic starting and stopping of transactions in the TestContext framework