Unit testing and declarative transactions with Spring and Hibernate

I’ve developed a simple service for creating users in an application, which I want to unit test. The service’s methods are declared as transactional with Spring metadata (In my case, I’m using annotations).

public class UserServiceImp implements UserService {
  @Transactional
  public void createUser(User u) {
    ...
  }
}

When unit testing, I don’t want to get the service through Spring… I want to test the real class, so I create it. This means that Spring doesn’t have a chance for adding the instrumentation (a proxy in my case) to the service class.

The result is that I get one transaction for each of the HibernateTemplate invocations performed inside the testing methods and service implementation, and the transaction is always rolled back (nothing is persisted to the database). In case you wondered, that’s a bad thing… for example, if I want to test that user creation correcly detects attempts to duplicate a user, I need to add a user and assert that a second attempt fails.

Update: I’ve written a new post about a better way for testing this scenario.

The solution is to use a TransactionTemplate and include all the test code inside its doInTransaction method.

I find it useful (albeit noisy) to throw a RuntimeException as the last thing inside the transaction, forcing a rollback. This way I’m assured that my test database is never modified and tests are repeatable. Just pay attention to the excepciont thrown and catch it to avoid signaling a test failure.

public void testSomething() throws Exception {
  TransactionCallback transac = new TransactionCallbackWithoutResult() {
    protected void doInTransactionWithoutResult(
      org.springframework.transaction.TransactionStatus status) {
      ...
      throw new RuntimeException("Rollback forced");
    }
  }
  PlatformTransactionManager ptm =
    (PlatformTransactionManager) ctx.getBean("txManager");
  try {
    new TransactionTemplate(ptm).execute(transac);
  } catch (RuntimeException e) {
    if (e.getMessage().equals("Rollback forced")) {
      /* Swallow exception. */
    } else {
      throw e;
    }
  }
}

I’m aware that there are some things I should not be doing… specially accessing the Spring context from my unit test (I keep a different configuration file for those, though), but I’m not sure about the implications of doing it this way… it just looked as the easier and quicker path.

How is the rest of the world testing this? I’ve read about using mock objects to avoid hitting the database, but I haven’t seen anything which can work with the Spring+Hibernate combo out of the box.

Any suggestions?

About these ads

  • My Open Source

  • Twitter Updates

    Error: Twitter did not respond. Please wait a few minutes and refresh this page.

  • Enter your email address to follow this blog and receive notifications of new posts by email.

    Join 9 other followers

  • Flickr Photos

    Apertura Agile Open Spain 2011 - 32

    Apertura Agile Open Spain 2011 - 31

    Apertura Agile Open Spain 2011 - 30

    More Photos

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: