Manualy Execute Code in Transaction in Test with Rollback
We Are Using Spring Boot 2, and in Our Integration Tests We Need Manually Execute Some Code in Transaction, and on the End of Transaction and After Asserts, We...
we are using spring boot 2, and in our integration tests we need manually execute some code in transaction, and on the end of transaction and after asserts, we want rollback that transaction.
We are using explicit defined transactions instead of @Transactional because sometimes we need execute in test 2 transactions.
here is sample of test:
@Test
public void fooTest() {
// transaction 1
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// some code in transaction
}
// transaction 2
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// some code in transaction
}
// here I need rollback these transactions for clean db for another tests
}
can you tell me how to use rollback in our case to rollback both transactions? It is little older code which we maintain so if it possible to do it better in boot 2 I will be gratefull for any advice. We just have to execute 2 transactions in one test.
2 Answers
Store references to each TransactionStatus in AtomicReference and rollback them with transaction manager after the test.
@Test
void test() {
final AtomicReference<TransactionStatus> first = new AtomicReference<>();
final AtomicReference<TransactionStatus> second = new AtomicReference<>();
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// some code in transaction
first.set(status);
}
});
// transaction 2
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// some code in transaction
second.set(status);
}
});
transactionTemplate.getTransactionManager().rollback(first.get());
transactionTemplate.getTransactionManager().rollback(second.get());
}
If you really need to start two transactions that run independently, doing some work on each one, and you need to check some asserts after they have both run, but before either has committed or rolled back - and then need to commit or rollback both after the asserts, you would need to set up your own multithreaded structure... Something like:
execute(AssertCallback aC, TransactionCallback ... tCs);
This method would start a thread for each of the tCs. Each thread would call the callback method in each one, and on return block on a barrier until all the threads executing tCs get to that same point. The main thread would also be waiting for all the tC threads to block, and then it would run the aC callback, and when that returns, release all the tC threads so they can commit/rollback and exit.
It all seems a little weird, in that the aC callback could not "see" any of the work done by any of the tC callbacks, since that's in transactions that have not been committed yet.
I've never seen this implemented... But, in case you want to see what I was talking about, take a look at the parallel-test project here: