Announcement Announcement Module
Collapse
No announcement yet.
TransactionalTestExecutionListener with rollback=false does not rollback on exception Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • TransactionalTestExecutionListener with rollback=false does not rollback on exception

    I have transactional test cases:

    Code:
    @ContextConfiguration(locations={"/applicationContext.xml"})
    @TestExecutionListeners(TransactionalTestExecutionListener.class)
    @TransactionConfiguration(defaultRollback=false)
    @Transactional
    public class TestCase extends AbstractTestNGSpringContextTests {
        @Autowired
        private UserService userService;
    
        @Test
        public void test1() {
           ...
           this.userService.doSomethingTransactional(...);
           ...
           this.userService.doSomethingElseTransactional(...);
        }
    }
    
    @Service
    public class UserService {
       @Transactional(readOnly = false)
       public void doSomethingTransactional(...) {
          ...
       }
    }
    Works fine in the "good" case. The UserService participates in the transaction declared on test1() (via the class level annotation @Transactional) and the data is being committed at the end of the test1 method.
    Now in a "bad" case, either when an internal exception happens or some Assert... statement fails, I want the transaction being rolled back. The TransactionalTestExecutionListener, however, either rolls back all transactions or commits them all, depending on the configuration. It does not take exceptions into account.
    When changing the code of the TransactionalTestExecutionListener like so:
    Code:
    public class TransactionalTestExecutionListener extends AbstractTestExecutionListener {
       ...
       protected final boolean isRollback(TestContext testContext) throws Exception {
          ...
          if (testContext.getTestException() != null)
             rollback = true;
          return rollback;
       }
       ...
    }
    it does pretty much what I wanted. Unfortunately, I need to copy the whole code of TransactionalTestExecutionListener and patch it in my own class, as there are no suitable (accessible) override points.

    Now my questions:
    Did I miss something fundamental?
    Is it not a common use case to have a commit for successful test cases and a rollback for exceptions?
    Is there another way to get the desired behavior without patching framework classes?

    Regards,
    Flo

  • #2

    Is it not a common use case to have a commit for successful test cases and a rollback for exceptions?
    There is and that is don't use the TransactionalTestExecutionListener .. That way you get the normal behavior as you would in your application...

    Comment


    • #3
      Thanks for your answer. Well, without using it, I'm losing the transactional context around test methods and the JPA entity manager is being closed after each and every service call (or is there some other alternative that I'm missing?)
      If not, I still think it would be great to have an optional property on the TransactionalTestExecutionListener to allow a more fine-grained control over rollback behavior with exceptions (or, as a simple alternative, what about making isRollback overridable?)

      Comment


      • #4

        Thanks for your answer. Well, without using it, I'm losing the transactional context around test methods and the JPA entity manager is being closed after each and every service call (or is there some other alternative that I'm missing?)
        Well you loose it but it will now behave as it behaves in your application (assuming you have correctly applied transactions to your service layer). If you need a transaction over several service methods then there shoulld be another method spanning those...

        You can always inject a TransactionTemplate into your test class for the cases you want the behavior you describe.

        Comment


        • #5
          Thanks for the quick answer. Yes, you are probably right - for testing a real-world application my approach would be broken (I'm testing some different JPA modelling approaches, not a real service layer), and I understand that you probably want your code to cover some well-defined purposes, not every edge case...

          Comment

          Working...
          X