Integration Testing with Spring – Data I

Gil Zilberfeld explains the data integration tests in Spring
Standard
In this series we're taking at Spring and its features supporting testing in general, and specifically integration tests.
Dependency injectionConfigurationsNested configurationsOrganizing configurations
Primary beansAvoiding problemsProfilesMocking I - Lifecycle
Mocking II - ResetMocking III - MockBeanData I - @SQLData II - JDBC
Data III - JPA

We’ve discussed what Spring can do for us in terms of integration tests so far, but nothing says more integration than database integration.

First, let’s define what we want in our integration tests, before we describe how the wonderful people of Spring and Spring Boot help us achieve it.

In our unit tests, we have complete isolation. In our integration tests, we want to have as much isolation as possible, given we’re already using Spring as a foundation. We’ve already discussed how to inject mocks, but the next step is to work with a database.

But working with a database ain’t cheap. We need an always-available database, whenever and where-ever we run the tests, so they won’t fail on any issue other than the actual code.

While it can be a shared database (like a dev-team database), there are conditions to be met. All kinds of integration tests are going to run at all times against this database. So it has to be up-to-date, in terms of both schema and data for all integration tests. Since we don’t control which integration test runs what and when, that creates a problem, if we update the schema, for example. Or, if one integration test relies on data that another integration test has deleted. Whenever there’s an integrity issue like this, our integration tests will fail, and it will take time to see what the problem is, and then we’ll curse ourselves that we didn’t use a private database.

So a private database it is. Sure, Oracle or Microsoft don’t give out those licenses away, but let’s say we’ll get around that. Even with our own database, with no other people interfering, there is still a data integrity issue. We’ll still need to manage everything ourselves.

It would be nice to have a throwaway database, wouldn’t it?. One, that once the integration tests have run, we don’t care what happens there, because there would be no footprint left.

I have another wish from my database. If we have a couple of features, one that accesses the Students table and the other needs the Teachers table, we don’t need both tables for testing each single feature separately. Loading the database with just the specific data for each integration test is easier to manage, much more than a whole database for all integration tests.

So our ultimate testing database is a disposable, private, test-specific, and as long as my wish list is still open, as fast as possible, we’d also like the same sweet feedback we get from unit tests.

A man can dream, no?

The good news is we can have all that and more.

First of all, meet H2, an in-memory (or embedded) relational database that’s pretty much built for integration testing. It’s equivalent to any relational database. It is private, because it runs in the same process as our code.
It is fast, because it doesn’t write to the disk. And once the process of our integration tests completes, it goes down without any trace (because data is not persisted).

H2 is not part of Spring, but you can select it to be included as part of a Spring Boot project. Once you got it there, we’re ready to go. Almost.

Using the database

Spring Boot does an awesome job in creating that “out of the box” feeling, including configuring the database. We can use the “spring.datasource.*” properties in the application.properties file, specifically the one that we keep in src/test/resources folder – the one that will run at test time, rather than the one in production. Or we can specify our data source in code, as we’ll see next time.

Spring Boot will start the embedded database server based on those properties. It creates a database for us. We can use that database for the purpose of the integration tests, which now can fill it. That’s where Spring comes with another awesome shortcut: the @SQL annotation for the test class.

As you can see, the @SQL annotation lets us run SQL scripts for creating a schema and filling in data (we can do whatever we want, really). We can also specify when to run these scripts, using the executionPhase member. The initialization scripts are executed to create the schema (at least), and some common data, will be there waiting for the integration tests. The scripts can be conveniently placed at the src/test/resources folder.

Why should we delete the data/schema at the end? After all, we said the database is disposable. While true, the lifecycle of the database is the whole integration test run. It maybe that after this test class, another test class will run, with different database needs. So let’s be good person-scouts and leave the grounds clean.

So that’s the setup stage. Now we need to actually to talk with the database in the integration tests themselves. We’ll split our discussion to JDCB and JPA flavors, but that’s next time.

Leave a Reply

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