简体   繁体   中英

An integration test fails when running with other integration tests which have transactional enabled

I am having a problem with an integration test which tests several services. I had to disable transactional to get the test working without any transactional related runtime errors. The integration test works fine when running it on its own but when running with other tests, which have transactional enabled, it creates this runtime error:

Running 48 integration tests... 43 of 48
Failure:  Tests the happy case flow of MyService.(MyServiceSpec)
org.springframework.transaction.HeuristicCompletionException: Heuristic completion: outcome state is rolled back; nested exception is org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has bee
n marked as rollback-only
Caused by: org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
        ... 4 more
Completed 43 integration tests, 1 failed in 0m 32s

I have concluded that the reason the runtime occurs is because of the other integration tests which use transactions, as I tested this by successfully running all tests which have transactional disabled; and was unsuccessful when running the test with a single integration test with transactional enabled.

How can I mix transactional and non-transactional integration tests in Grails?

Platform Details:

Grails-2.3.6 Windows 7 64 bit. JDK v6.

Ran into this with Grails 2.4.3 and after a bunch of debugging saw that in org.springframework.orm.hiberante4.HibernateTransactionManager.doGetTransaction() it calls TransactionSynchronizationManager.getResource(getSessionFactory()) and if there were other tests with transactional enabled then it will find a thread-bound SessionHolder with rollbackOnly set to true (since the previous test rolled back). So, the first time it tries to commit a transaction, it will see this and give the UnexpectedRollbackException you indicated.

I got around this by putting the following in the setUp() of the test that was marked as non-transactional:

Holders.grailsApplication.mainContext.getBeansOfType(SessionFactory.class).each { beanName, sessionFactory ->

    SessionHolder sessionHolder = TransactionSynchronizationManager.getResource(sessionFactory)

    if (sessionHolder) {
         sessionHolder.clear()
    }
}

Integration tests will do the following

  • start transaction
  • run test
  • rollback transaction

in general this will work and reset the database state to the state before the test. But if your test logic will treat transactions in a specific way you will run into problems. One example would be creating your own transaction inside the test code with propagation=REQUIRES_NEW. Whatever you did in that transaction can not be rolled back by the test logic.

Usually code like that will break the independence between tests. The only really safe way is to have each test start with an empty db and insert what is needed ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM