Hibernate integration testing strategies

Introduction

I like Integration Testing; it’s a good way to check what SQL queries Hibernate generates behind-scenes. But Integration Tests require a running database server, and this is the first choice you have to make.

Using a production-like local database server for Integration Testing

For a production environment, I always prefer using incremental DDL scripts, since I can always know what version is deployed on a given server, and which are the newer scripts required to be deployed. I’ve been relying on Flyway to manage the schema updates for me, and I’m very content with it.

On a small project, where the amount of Integration tests is rather small, you can employ a production-like local database server for testing as well. This is the safest option since it guarantees you’re testing against a very similar environment with the production setup.

The major drawback is tests speed. Using an external database implies an additional timing cost, which may easily get out of control on a large project. After all, who’s fond about running a 60 minutes test routine on a daily basis?

In-memory database Integration testing

The reason why I chose to use in-memory databases for Integration Testing is to speed-up my tests running time. This is one aspect affecting tests running time, and there are many others that may affect you, like destroying and recreating a Spring application context, containing a large number of bean dependencies.

I’ve been using two in-memory schema generation strategies, both of them have pros and cons, which I am going to explain as follows.

Making use of hibernate.hbm2ddl.auto=”update”

Hibernate is very flexible when it comes to configuring it. Luckily we can customize the DDL generation using the “hibernate.hbm2ddl.auto” SessionFactory property.

The simplest way to deploy a schema is to use the “update” option. This is useful for testing purposes. I wouldn’t rely on it for a production environment, for which incremental DDL scripts is a better approach.

So, choosing the “update” option is one choice for Integration Testing schema management.

I think Bitronix is one of the most reliable tools I’ve ever worked with. When I was developing JEE applications, I was taking advantage of the Transaction Manager supplied by the Application Server in use. For Spring-based projects, I had to employ a stand-alone Transaction Manager, and after evaluating JOTM, Atomikos, and Bitronix, I settled for Bitronix. That was 5 years ago, and ever since I’ve deployed several applications, making use of it.

I prefer using XA Transactions even if the application is currently using only one Data Source. I don’t have to worry about any noticeable performance penalty of employing JTA, as Bitronix uses 1PC (One-Phase Commit) when the current Transaction uses only one enlisted Data Source. It also makes possible of adding up to one non-XA Data Source, thanks to the Last Resource Commit optimization.

When using JTA, it’s not advisable to mix XA and Local Transactions, since not all XA Data Sources allow operating inside a Local Transaction, so I tend to avoid this as much as possible.

Unfortunately, as simple as this DDL generation method is, it has one flaw which I am not too fond of. I can’t disable the “allowLocalTransactions” setting since Hibernate creates the DDL script and updates it outside of an XA Transaction.

Another drawback is that you have little control over what DDL script Hibernate deploys on your behalf, and in this particular context I don’t like compromising flexibility over convenience.

If you don’t use JTA, and you don’t need the flexibility of deciding what DDL schema would be deployed on your current database server, then the hibernate.hbm2ddl.auto=”update” is probably your rightful choice.

Flexible schema deploy

This method consists of two steps. The former is to have Hibernate generating the DDL scripts, and the latter is to deploy them in a customized fashion.

To generate the DDL scripts I have to use the following Ant task (even if being run through Maven), and this is because there is no Hibernate 4 Maven plugin I could use at the time of writing: