0 Replies Latest reply on Mar 27, 2007 10:25 PM by Ross Bagley

    Explicit transaction boundaries in tests

    Ross Bagley Newbie

      Basic problem: I've been trying to find it, but I don't see a callback-based approach to explicit transaction demarcation that I can use in my tests.

      Most of the existing test code suggests that the "right" way to make part of a test method transactional is to have an EntityManagerFactory nearby and to structure the test method like this:

      @Test
      public void testMethod() throws Exception {
      
       (non-transactional code here)
      
       EntityManager em = getEntityManagerFactory().createEntityManager();
       em.getTransaction().begin();
      
       (transactional code here)
      
       em.getTransaction.commit();
       em.close();
      
       (non-transactional code here)
      
      }

      Which works okay, if you don't mind your test code directly interacting with the EntityManagerFactory, EntityManager and the Transaction; and if you're testing non-failure cases; and if the test passes without any problem...

      Some of those issues might lead you to write slightly safer transactional code, which might look like this:
      @Test
      public void testMethod() throws Exception {
      
       (non-transactional code here)
      
       EntityManager em = getEntityManagerFactory().createEntityManager();
       try {
       em.getTransaction().begin();
      
       (transactional code here)
      
       em.getTransaction().commit();
       } catch (Exception e) {
       em.getTransaction().rollback();
       throw e;
       } finally {
       em.close();
       }
      
       (non-transactional code here)
      
      }

      I'm sure that a lot of us have seen transactional boilerplate like that, however, there are better ways to solve this problem. This is what I really want my test method to look like:
      @Test
      public void testMethod() throws Exception {
      
       (non-transactional code here)
      
       getTransactionManager().execute(new TransactionCallback() {
       public void doInTransaction() {
      
       (transactional code here)
      
       }
       });
      
       (non-transactional code here)
      
      }

      Which eliminates all of the problems with the initial approach with a lot less boilerplate than the second approach, lets me focus the test on exercising my code instead of managing the EntityManager (or Session, or whatever), lets me use the exact same transaction manager in testing as in production, and with a few further refinements, can give me full access to different transaction semantics (REQUIRES_NEW, ALLOWS, etc.) if I need them.

      So, my question is: did I miss the part of the documentation where Seam supports some means of using transactional callbacks for explicit management of transactional code or should I spend a couple of days and adapt one of my pre-Spring, SLSB-based frameworks to do this in EJB3 and Java5?

      I must admit, that as long as this pattern has been around, I'm a little astonished that I don't see documentation for something like it in Seam re: transactions or if I'm just unable to read the docs: that the testing examples don't use it. I've been using transactional callbacks to insulate developers from transaction complexity since I first used Hibernate back in the 1.x days (2002) and I didn't invent it back then. Spring's TransactionManager provides a similar implementation for explicit demarcation (my example is pretty much "the Spring way"), but the pattern was around long before Spring appeared on the scene.

      One other possibility is to bring Spring into my app and use Spring's TransactionManager for explicit and AOP-implemented transactions. Upside: I could get rid of a LOT of SLSB's that are only doing duty as transaction markers now. Upside: when I don't need true JTA transactions (which is never in this app), I don't have to worry about them. Downside: yet another external dependency. Downside: two different IOC controllers to configure properly. Neither up nor down: if I'm bringing in Spring for transactions, I might as well bring Spring WebFlow to handle conversational scoped beans and then I'm running out of responsibilities to justify the weight of JBoss/Seam...

      Thoughts?
      Ross