Skip to content

TransactionalTestExecutionListener uses cached TransactionManager (TM), even after ApplicationContext that created the TM has been marked 'dirty' and closed. [SPR-4638] #9315

Closed
@spring-projects-issues

Description

@spring-projects-issues

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:

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions