Skip to content

JUnit testing Hibernate and Spring

Here’s a nice recipe for unit testing Spring configured Hibernate. It allows me to neatly test my Spring configured DAOs and reuse a lot of the Hibernate Session and Transaction configuration beans from my production code. This saves having to rewrite it for my tests and also makes the tests more realistic. I’d rather test my production code rather than use mocks as far as possible.

My App code

I use Spring to create DAOs and configure the datasources, Hibernate session and transaction behaviour. As all of this is just general behaviour rules, I don’t want it cluttering my Java application code.

Briefly, here are the important Spring configured beans:

My datasources (just pulled from JNDI)…

<bean id="myProductDS"
 p:proxyInterface="javax.sql.DataSource"
 p:jndiName="java:comp/env/jdbc/MyProductDB"/>

… and my Hibernate properties …

<bean id="hibernateProperties">
  <property name="properties">
  <props>
    <prop key="hibernate.hbm2ddl.auto">validate</prop>
    <prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
    <prop key="hibernate.show_sql">true</prop>
  </props>
  </property>
</bean>

… and my Hibernate session…

<bean id="sessionFactory">
  <property name="dataSource" ref="myProductDS"/>
  <property name="hibernateProperties" ref="hibernateProperties"/>
  <property name="mappingResources">
  <list>
    <value>Spanners.hbm.xml</value>
  </list>
  </property>
</bean>

… and my DAOs …

<bean id="spannersDAOTarget">
  <property name="sessionFactory" ref="sessionFactory"/>
</bean>

… and my transactions …

<bean id="spannersDAO" parent="abstractTransactionProxy">
  <property name="target" ref="spanenrsDAOTarget"/>
</bean>

<bean id="abstractTransactionProxy" abstract="true">
  <property name="transactionManager" ref="transactionManager"/>
  <property name="transactionAttributes">
    <props>
      <!-- Read methods - Mark as read only so they don't create unncecessary transactions. They can run within an existing transaction too. -->
      <prop key="get*">PROPAGATION_SUPPORTS,readOnly</prop>
      <prop key="find*">PROPAGATION_SUPPORTS,readOnly</prop>
      <prop key="count*">PROPAGATION_SUPPORTS,readOnly</prop>
      <!-- Write methods (everything else). Must be run in a transaction. A new transaction will be created if necessary. -->
      <prop key="*">PROPAGATION_REQUIRED</prop>
    </props>
  </property>
</bean>

Very neat. Setting up the Session with the TransactionProxyFactoryBean in this way allows me to avoid all the Session / Transaction boilerplate.  Sessions and Transactions never appear in application code. I just inject the spannerDAO class into my services and call methods.

My Tests

I’d like JUnit to test my DAOs and I’d quite like to configure it in the same way. I need to make a couple of changes though.

First I don’t want to test against the external datasources. That is, I don’t want to depend on an Oracle or SQLServer instance existing somewhere on my local machine or on the network. This just adds a fiddly dependency to every machine capable of building my project. In particular, this requires installing database client software to the ‘clean’ build machine. I’d rather not. Instead, I use an in memory datasource – HyperSQL in this case. This can be run entirely from the JUnit test fixture.

Second, I don’t really want to maintain a test database at all. Whether or not my database is in memory, something needs to create it and load a schema. Fortunately, Hibernate neatly takes care of this for you. Simply set the ‘hbm2ddl.auto’ property to ‘create’. Then it will create the database schema at startup time. That is, when the test fixture is loaded and the HyperSQL database starts up.

As a result, I replace the database connection beans with the following:

<bean id="myProductDS">
  <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
  <property name="url" value="jdbc:hsqldb:mem:test-myProductDB"/>
  <property name="username" value="sa"/>
  <property name="password" value=""/>
</bean>

<bean id="hibernateProperties">
  <property name="properties">
    <props>
      <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
      <prop key="hibernate.show_sql">true</prop>
      <prop key="hibernate.hbm2ddl.auto">create</prop>
      <prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</prop>
    </props>
  </property>
</bean>

Every other bean can be used as is so just leave them be.

All that’s left is to create a test class for the DAOs. We want the test class to see the Spring beans so use the Spring’s AbstractSingleSpringContextTests class, overriding the getConfigLocations method.

public class SpannersDAOTest extends AbstractSingleSpringContextTests {

    protected String[] getConfigLocations() {
        return new String[]{
          "spring-hibernate.xml", // Containing Hibernate session bean, transaction config and DAOs
          "spring-test-dataSources.xml" // Containing HyperSQL datasource and test Hibernate properties
    };
}

Now all that’s left is to get the DAO bean and start making calls to it.

public void testSpannersDAO() {
  SpannersDAO spannersDAO = (SpannersDAO)applicationContext.getBean("spannersDAO");
  spannersDAO.addSpanner(...);
  ... etc
}

One final point: You could argue that this method of testing is flawed. My production database is SQLServer but I’m testing against HyperSQL. Also, unless I have the setup of the tests create a bucket load of sample data, I’m running my tests against a fresh, empty database. Hardly realistic.

It is true that the above method does not adequately test database functions. However, that is not the aim of these particular tests. They are intended to test the DAO code and Hibernate mappings. To properly test the database functions, we’d use the standard (SQLServer) datasource bean, connected to some dedicated test database. This database would exist within the test environment for the sole purpose of this sort of test. There are a few articles out there on suggested ways to do this, but none of them seem pleasant. Once you start connecting to ‘real’ databases you start getting into all sorts of hassle. You need a database available and maintained alongside your code. You need some way of guaranteeing that the database is ‘consistent’ when you run your tests. So either you reinitialize the database before every test run or you ensure that your test cases always rollback and never commit (which isn’t realistic either). I’m of the opinion that this is not worth the hassle.

But it’s worth keeping in mind the object of the exercise. We are unit testing the DAOs and Hibernate mappings only. We are NOT integration testing the database. So we can be reasonably sure that our DAOs function the way we expect. We can however say nothing about the correctness of our database schema nor anything about what is actually inserted or retrieved from the database. That must be tested seperately.

EDIT: This recipe was expanded upon in my post on DbUnit

Published inHow ToJava TechnologiesTesting

One Comment

  1. […] decided to revisit the JUnit testing Hibernate and Spring recipe that I posted a while back. A problem with the previous recipe is that it did not provide […]

Leave a Reply

Your email address will not be published. Required fields are marked *