I'm using Spring 3.0.5 and Junit 4.8.2 Is it possible to use multiple transaction managers during tests?
Basically I'm try for something like this. I need to add and remove content from two separate databases during the tests.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/applicationContext-test.xml" })
@TransactionConfiguration(transactionManager = "txMgrA", defaultRollback = true)
@TransactionConfiguration(transactionManager = "txMgrB", defaultRollback = true)
@Transactional
public class SampleTest {
...
}
Since Java will not allow multiple annotations of the same type per element, you must find another way to configure it. @TransactionConfiguration
is interpreted by TransactionalTestExecutionListener
, whose getTransactionManager
method only returns a single PlatformTransactionManager. It looks at @Transactional but seems to ignore the value
qualifier that was added in Seam 3.0.
@Transactional itself only supports a single transaction manager. How is the real application configured? You must be using @Transactional("<qualifier>")
(as in the docs), right?
If you just use @Transactional with different tx managers on different methods, then the simplest solution is to just split your test class.
Are you nesting the transactions? That is, you have @Transactional("tm1") on one method, which calls a nested method that has @Transactional("tm2")? Sounds a little unusual. You could try to set up your test in the same way -- have two test @Services, each with the appropriate @Transactional annotations, that are proxied with tx:advice as usual. The outer service sets up the outer txn; the inner service sets up the inner txn and contains the actual test code. You can't use @Rollback, but hey, hacks ain't pretty.
Another option would be to create your own PlatformTransactionManager that delegates to the two other managers (for testing purposes only).
Maybe better would be to just give up and manually manage the two transactions in the test's @Before/@After methods.
Best would be to use JTA global transactions. Hopefully you're not actually nesting separate transactions and this is all moot ;)
Looking at Spring 5 TransactionalTestExecutionListener
implementation, it looks it only supports one TransactionManager
per thread, what seems to be a design flaw of this listener, probably the same as you came across in 2011 :)
However, currently it's possible to workaround this using ChainedTransactionManager
. If you have multiple transaction managers, you can define yet another transaction manager in your test context:
@Configuration
class TestTransactionConfig {
@Bean("testTransactionManager")
public PlatformTransactionManager chainedTransactionManager(
@Qualifier("transactionManager1") PlatformTransactionManager transactionManager1,
@Qualifier("transactionManager2") PlatformTransactionManager transactionManager2
) {
return new ChainedTransactionManager(transactionManager1, transactionManager2);
}
}
Now, you can define your base class for tests using this transaction manager:
@RunWith(SpringRunner::class)
@Transactional("testTransactionManager")
class BaseTransactionalTest {
}
For all derived classes all test methods will now be wrapped in both transactions, which will be finally rolled back by TransactionalTestExecutionListener
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With