Unit testing JPA - Hibernate components with in-memory H2 database
The goal
Be able to unit test database related components without the need to set up and entire RDBMS ecosystem. Enabling integration tests to be run by a continuous integration server.
The solution
Create a database on the fly, in-memory. This is achieved by using Maven, Springframework and Hibernate together.
Dependencies
I have tested with Derby (former Cloudscape). Derby provides an EmbeddedDriver. However files are still created in the filesystem.
HSQLDB should be able to do the trick but I was not able to get the configuration right due to a lack of proper documentation.
H2 delivered the final solution. I added the following dependency to my pom.xml
:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.160</version>
<scope>test</scope>
</dependency>
Test class
Personally I am a big fan of TestNG. Mainly for the ability to group tests. Spring 2.5.x now has excellent annotation based support for TestNG.
If you your boss or your customer requires you to use JUnit then you can subclass AbstractTransactionalJUnit4SpringContextTests
instead.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.testng.annotations.Test;
@Test(groups = { "integration" })
@ContextConfiguration(locations = { "/application-context.xml", "/test-application-context.xml" })
@TransactionConfiguration(defaultRollback=true)
public class TestSomeService extends AbstractTransactionalTestNGSpringContextTests {
// bunch of test methods
}
After the main application-context.xml
is loaded I load test-application-context.xml
which overrides a couple of bean definitions.
Test application context
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.h2.Driver"/>
<property name="url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"/>
</bean>
<bean id="jpaAdaptor" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" />
<!-- Let Hibernate generate the DDL for the schema -->
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
</bean>
</beans>
Initialize database schema
Hibernate has the ability to create a database schema on the fly the first time the SessionFactory
is loaded. This is configured in two places. First the HibernateJpaVendorAdapter
needs to be told to generateDdl
. Second in the file META_INF/persistence.xml
(the default JPA persistence context definition) I set the hibernate.hbm2ddl.auto
to create.
<persistence-unit name="some-unit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<!-- Auto detect annotated model classes -->
<property name="hibernate.archive.autodetection" value="class" />
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="create"/>
</properties>
</persistence-unit>
To fill the database with initial data add a file called import.sql
containing insert statements to the classpath. To get a grasp of what hbm2ddl is doing we need to add the following logging configuration:
<logger name="org.hibernate.tool.hbm2ddl" additivity="false">
<level value="debug" />
<appender-ref ref="out" />
</logger>