Search code examples
javaspringjpatransactionstestng

Disabling transaction on spring testng test method


I am using TestNG together with Spring and JPA. So far I am using to test database stuff my test class which extends AbstractTransactionalTestNGSpringContextTests. With @TransactionConfiguration(defaultRollback = true) everything works fine and I do not need to cary about cleanup. Spring creates a default transaction on the beginning of each of my test method which is than rollbacked. This is a very neat trick to solve famous "Transactional tests considered harmful" problem.

Unfortunately I need for one method in this class (one test) to not have this default transaction. This is because this test method simulates batch processing and I have multiple transactions inside it which are in production independent. The only way I was able to simulate and solve problem is to configure those inner transactions with Propagation.REQUIRES_NEW but this I do not want to have in production code. Is there some way to disable Spring transaction for my particular test method (so I do not need to use Propagation.REQUIRES_NEW but Propagation.REQUIRED in my service methods) ?


Solution

  • I have found that by executing the body of my test in separate thread prevent Spring from transaction. So the workaround solution is something like:

        @ContextConfiguration(classes = { test.SpringTestConfigurator.class })
        @TransactionConfiguration(defaultRollback = false)
        @Slf4j
        @WebAppConfiguration
        public class DBDataTest extends AbstractTransactionalTestNGSpringContextTests {    
        /**
         * Variable to determine if some running thread has failed.
         */
        private volatile Exception threadException = null;
    
       @Test(enabled = true)
        public void myTest() {
            try {
                this.threadException = null;
                Runnable task = () -> {
                    myTestBody();
                };
                ExecutorService executor = Executors.newFixedThreadPool(1);
                executor.submit(task);
                executor.shutdown();
                while (!executor.isTerminated()) {
                    if (this.threadException != null) {
                        throw this.threadException;
                    }
                }
                if (this.threadException != null) {
                    throw this.threadException;
                }
            } catch (Exception e) {
                log.error("Test has failed.", e);
                Assert.fail();
            }
        }
    
     public void myTestBody() {
        try {
            // test body to do
        }
        catch (Exception e) {
           this.threadException = e; 
        } 
     } 
    }