1
votes

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
Please check if Programmatic Transaction Management in Spring Testing works in your case: docs.spring.io/spring/docs/current/spring-framework-reference/… and baeldung.com/spring-test-programmatic-transactionsibexit
"execute two transactions in one test" is not the same as "roll back both transactions" - do you need to do the latter, or the former? If the latter, just have your callback end with a call to status.setRollbackOnly(true) - or throw some runtime exception - that will make the template roll back the transaction ... (and wrap the calls to execute() in try/catch blocks, so that they both run :-)).moilejter
Hmm - but you would need to check your asserts within the calbacks...moilejter

2 Answers

1
votes

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());
}
0
votes

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: https://github.com/moilejter/examples.git