We provide a variety of services including:

·         Training classes for Spring, Hibernate and Acegi Security

·         Jumpstarts to get your project off to the right start

·         Reviews to improve your architecture, code and development process

For more information visit our website: http://www.chrisrichardson.net

 

Home > Knowledge > Testing > ORMUnit Cookbook

 

ORMUnit Cookbook

Chris Richardson, chris@chrisrichardson.net

In order to make it easier to write tests for a Hibernate-based persistence tier, I’ve written a simple JUnit extension called ORMUnit. It provides the following subclasses of JUnit TestCase:

·         HibernateORMappingTests: For testing a Hibernate object/relational mapping

·         HibernatePersistenceTests: For testing Hibernate objects and queries

·         HibernateSchemaTests: For testing the database schema

HibernateORMappingTests simplifies the task of testing the object/relational mapping. It provides methods for making assertions about the mapping. For example, they make it easy to write a test that verifies that all of a class’s fields or properties are mapped to the database.

HibernatePersistenceTests makes it easier to write tests for persistent objects and queries. It takes care of initializing the database; provides access to HibernateTemplate; and manages transactions.

HibernateSchemaTests makes it easy to verify that the database schema matches the O/R mapping.

Installation

There are two ways to include the ORMUnit jar in your application. If you are using Maven you can get it from the following repository:

 

<repositories>

   <repository>

  <id>pia-repository</id>

  <url>http://www.pojosinaction.com/repository</url>

  </repository>

<repositories>

 

<dependency>

   <groupId>net.chrisrichardson</groupId>

   <artifactId>ormunit-hibernate</artifactId>

   <version>1.0-SNAPSHOT</version>

</dependency>

 

Alternatively, you can get the source code from http://code.google.com/p/ormunit/

TODO – one day there will be a downloadable zip

Configuration

The ORMUnit test classes are intended to be used in a Spring application (however, see below on how you can use other ORMUnit classes without Spring). They extend AbstractDependencyInjectionSpringContextTests. They define various public setters including setSessionFactory().

Testing the O/R Mapping

Let’s first look at how to test the O/R mapping. This may seem strange at first but testing the O/R mapping is a quick and easy way to detect some kinds of bugs. For example, one common mistake is forgetting to write the O/R mapping for a newly added field or property. You could detect this bug by writing a test that saves and object and verifies that corresponding column contains the expected value. However, that can be a lot of work and the tests might take a while to execute. In comparison, testing the mapping is a lot easier and the tests are fast.

Here is an example of such as test:

 

public class ExampleMappingTests

         extends HibernateORMappingTests {

 

  @Override

  protected String[] getConfigLocations() {

    return new String[] {"classpath:appctx/context.xml" };

  }

 

  public void testAllClassesMapped() {

      assertAllClassesMapped();

  }

}

 

This test will throw an exception if any persistent class contains an unmapped.

By default, ORMUnit checks for unmapped properties but you can check for unmapped fields by calling setAccessStrategy():

 

public class ExampleMappingTests

         extends HibernateORMappingTests {

 

  protected void onSetUp() throws Exception {

    super.onSetUp();

    mappingChecker.setAccessStrategy(AccessStrategy.FIELD);

  }

 

  public void testAllClassesMapped() {

      assertAllClassesMapped();

  }

}

Testing the database schema

ORMUnit can also verify that the database schema matches the O/R Mapping. It’s not uncommon for the two to quietly diverge. Hibernate can be configured to check but if you are not using that option then this can be a quick and easy way to check.

Here is an example of a test:

 

public class PointlessSchemaTest extends HibernateSchemaTests {

 

  @Override

  protected String[] getConfigLocations() {

    return new String[] { "classpath:appctx/context.xml" };

  }

 

  public void testSchema() throws Exception {

    assertDatabaseSchema();

  }

 

}

 

The test will fail if any tables or columns are missing from the database schema.

Writing CRUD tests for persistent objects

ORMUnit defines the class HibernatePersistenceTests, which makes it easier to write tests that CRUD persistent objects.

A simple example

Here is an example of a really simple CRUD test that saves a persistent object and then loads it.

public class CustomerPersistenceTests extends HibernatePersistenceTests<CustomerPersistenceTests> {

 

  @Override

  protected String[] getConfigLocations() {

    return new String[] { "classpath:appctx/context.xml" };

  }

 

 

  public void testSimple() {

    Customer c = new Customer("John Doe", new Address("1

                       High Street", null, "Oakland", "CA",

                       "94610"));

    int id = (Integer) save(c);

   

    Customer c2 = get(Customer.class, id);

    assertNotNull(c2);

  }

}

There are a few notable things. The test calls save() to persist the object and get() to load it. These methods are provided by HibernatePersistenceTests. Note that you don’t need to cast the return value of get().

Transaction handling

By default, HibernatePersistenceTests executes each test in a transaction which is rolled back at the end. This improves performance and ensures that the database is unchanged, ready for the next test. However, because objects are cached in the Hibernate Session and changes are queued until flush-time the test might not do exactly what you think it does. In the above example, the get() does not actually execute a SQL SELECT because the customer is already in the Session.

HibernatePersistenceTests provides a couple of ways to address this problem:

·         Automatic flushing and clearing of the Session

·         Committing transactions but resetting the database at the start of a test

Automatic flushing and clearing of the session

One approach is to flush() and clear() the session between each step of the test. Here is one way to write a test that does this.

public class CustomerPersistenceTests extends

     HibernatePersistenceTests<CustomerPersistenceTests> {

 

  public void testUpdateSimpleV1() {

    doWithTransaction(new TxnCallback(){

 

      public void execute() throws Throwable {

        Customer c = new Customer("John Doe",

               new Address("1 High Street",

               null, "Oakland", "CA", "94610"));

        id = save(c);

      }});

 

    doWithTransaction(new TxnCallback(){

     

 

      public void execute() throws Throwable {

        Customer c2 = get(Customer.class, id);

        assertNotNull(c2);

      }});

 

  }

 

This test uses doWithTransaction() to demarcate each step of the test. It flushes and clears the Session.

One drawback of doWithTransaction() is that it’s a little ugly. Here is a simpler (yet more magical way) to accomplish the same thing.

 

public class CustomerPersistenceTests extends

     HibernatePersistenceTests<CustomerPersistenceTests> {

 

  public void testUpdateSimpleV2() {

    Serializable id = txnThis.saveCustomer();

    txnThis.loadCustomer(id);

  }

 

  Serializable saveCustomer() {

    Customer c = new Customer("John Doe", new Address("1  

                       High Street",

                       null, "Oakland", "CA", "94610"));

    return save(c);

  }

 

  void loadCustomer(Serializable id) {

    Customer c2 = get(Customer.class, id);

    assertNotNull(c2);

  }

 

In this example, txnThis (a protected variable defined by HibernatePersistenceTests) is a proxy that executes the method within a doWithTransaction().

Committing transactions

An alternative approach, which avoids the problems caused by a long-lived Hibernate sessions, is to commit the transaction and not use a single transaction (and session) for the duration of the test. The ORMUnit transaction handling mechanism is pluggable and is configured by a HibernatePersistenceTestsStrategy that’s injected into HibernatePersistenceTests. The default implementation is RollbackTransactionHibernatePersistenceTestsStrategy, which implements the strategy described above of rolling back the transaction. The other implementation is SimpleHibernatePersistenceTestsStrategy, which commits transactions.

When using this strategy the tests are not wrapped in a transaction. The doWithTransaction() method executes the callback in a real transaction. Moreover, Hibernate Sessions are only opened when necessary: by calls to HibernateTemplate and doWithTransaction(). This means that objects are cached for shorter periods of time and the session is flushed more regularly.

One drawback of committing transactions is that tests can change the database. To solve this problem SimpleHibernatePersistenceTestsStrategy resets the database at the start of each test by using a DatabaseResetStrategy. There is an implementation of this class that recreates the schema using Hibernate hbm2dll.

You would, for example, configure the HibernatePersistenceTestsStrategy and DatabaseResetStrategy by including the following bean definition in the application context:

<bean id="hibernatePersistenceTestsStrategy"   

  class="net.chris…SimpleHibernatePersistenceTestsStrategy">

    <constructor-arg>

      <bean

        class="net.chris…ResetDatabaseByRecreatingSchemaStrategy" />

    </constructor-arg>

    <constructor-arg>

      <bean

    class="org.springframework.transaction.support.TransactionTemplate"

     >

        <property name="transactionManager"

          ref="transactionManager" />

      </bean>

    </constructor-arg>

</bean>

Note regardless of the strategy your tests would still use doWithTransaction() or txnThis to demarcate steps of the test.

Using ORMUnit outside of a Spring application

ORMUnit was intended to be used in a Spring application. However, it’s quite easy to use some parts of ORMUnit without Spring since the Spring-specific test classes are simply wrappers around other classes:

Spring-specific class

Non-Spring classes

HibernateMappingTests HibernateORMappingTests

HibernateMappingChecker

MappedClassChecker

HibernateSchemaTests

HibernateSchemaChecker

HibernatePersistenceTests

HibernatePersistenceTestsStrategy and DatabaseResetStrategy

 

Further examples