Question

I am using TestNG to test persistence Spring modules (JPA+Hibernate) using AbstractTransactionalTestNGSpringContextTests as a base class. All important parts @Autowired, @TransactionConfiguration, @Transactional work just fine.

The problem comes when I am trying to run test in parallel threads with threadPoolSize=x, invocationCount=y TestNG annotation.

WARNING: Caught exception while allowing TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener@174202a] 
to process 'before' execution of test method [testCreate()] for test instance [DaoTest] java.lang.IllegalStateException:
Cannot start new transaction without ending existing transaction: Invoke endTransaction() before startNewTransaction().
at org.springframework.test.context.transaction.TransactionalTestExecutionListener.beforeTestMethod(TransactionalTestExecutionListener.java:123)
at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:374)
at org.springframework.test.context.testng.AbstractTestNGSpringContextTests.springTestContextBeforeTestMethod(AbstractTestNGSpringContextTests.java:146)

... Has anybody faced this problem?

Here is the code:

@TransactionConfiguration(defaultRollback = false)
@ContextConfiguration(locations = { "/META-INF/app.xml" })
public class DaoTest extends AbstractTransactionalTestNGSpringContextTests {

@Autowired
private DaoMgr dm;

@Test(threadPoolSize=5, invocationCount=10)
public void testCreate() {
    ...
    dao.persist(o);
    ...
}
...

Update: It seems that AbstractTransactionalTestNGSpringContextTests maintains transaction only for main thread when all other test threads don't get their own transaction instance. The only way to solve that is to extend AbstractTestNGSpringContextTests and maintain transaction programmatically (instead of @Transactional annotation) per each method (i.e. with TransactionTemplate):

@Test(threadPoolSize=5, invocationCount=10)
public void testMethod() {
    new TransactionTemplate(txManager).execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            // transactional test logic goes here
        }
    }
}
Was it helpful?

Solution

The transaction needs to be started in the same thread, here are more details:

Spring3/Hibernate3/TestNG: some tests give LazyInitializationException, some don't

OTHER TIPS

Don't you think this rather comes from org.springframework.test.context.TestContextManager not being thread safe (see https://jira.springsource.org/browse/SPR-5863)?

I faced the same issue when I wanted to launch my Transactional TestNG tests in parallel and you can see that Spring actually tries to bind the transaction to the right Thread.

However this fails randomly with this kind of errors. I extended AbstractTransactionalTestNGSpringContextTests with :

@Override
@BeforeMethod(alwaysRun = true)
protected synchronized void springTestContextBeforeTestMethod(
        Method testMethod) throws Exception {
    super.springTestContextBeforeTestMethod(testMethod);
}

@Override
@AfterMethod(alwaysRun = true)
protected synchronized void springTestContextAfterTestMethod(
        Method testMethod) throws Exception {
    super.springTestContextAfterTestMethod(testMethod);
}

(the key being the synchronized...)

and it is now working like a charm. I can't wait for Spring 3.2 though so that it can be completly parallized!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top