There are those moments you wish you could just start up a real database or system for your (integration) test. In many tests I’ve written I used H2 or HSQLDB to have a data storage for my tests. It starts up quickly and almost supports everything you need to do your repository test or any other test needing data storage. But when your project progresses you start using other ways to store your data other than standard SQL or you use dialect specifics to create your database. This is the moment you discover H2 or HSQLDB is not supporting your database vendor specific features and you can’t get your test running. For example the support for PostgreSQL in H2 or HSQLDB isn’t great, using TIMESTAMP in a SQL script already makes H2 or HSQLDB break. Yes, there are workarounds, but you rather not apply them to keep your code clean and simple. This is the moment you wish it is cheap to start up a real database instance you can test against, so you’re sure your code works in your production environment. You could install the database software locally, make some scripts to initialise the database and clean up afterwards. Or you can make scripts to do this in a Docker container. But what if there’s something which makes this even cheaper to setup?

TestContainers to save your day

Well, there is something to help you: the TestContainers project. With TestContainers you can startup your favourite database from a Docker container. TestContainers made a wrapper around docker to have an easy setup for your tests. And even better, is doesn’t only work with your favourite database, it works with any docker container you need in your test. Okay, I have to admit, the startup time for your test is longer in comparison with H2 or HSQLDB, but on the other hand you get a fully functional database instance.

How do I use it?

I’ll explain to you how to use a PostgreSQL TestContainer within a Spring Boot setup with some code examples. First add the TestContainers and PostgresSQL-TestContainers dependencies

dependencies {
    testCompile 'org.testcontainers:testcontainers:1.9.1'
    testCompile 'org.testcontainers:postgresql:1.9.1'
}

Now we have all to run the PostgreSQL container, but there are different ways to make use of it. You can define it as a datasource connection string or use @ClassRule in your test. When a datasource connection string is used, this will be the default for every test.

Using connection URL

spring.datasource.url=jdbc:tc:postgresql:9.6.8://localhost/organization
spring.datasource.username=postgres
spring.datasource.password=
spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver

Notice the tc:postgresql:9.6.8 part in the url, this tells TestContainers which database and version to use. Your test needs a little bit of setup to tell Spring not to create an in-memory database

@DataJpaTest
@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class MyDataTest {
    // your test logic
}

Using @Classrule

When using @ClassRule you have to let Spring know where to find the database, with your ApplicationContextInitializer you can override the previously set properties and let the started container define those properties

@DataJpaTest
@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@ContextConfiguration(initializers = MyDataTest.Initializer.class)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class MyDataTest {
    @ClassRule
    public static PostgreSQLContainer postgres = new PostgreSQLContainer();

    // your test logic

    public static class Initializer
        implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
            TestPropertySourceUtils.addInlinedPropertiesToEnvironment(configurableApplicationContext, "spring.datasource.url=" + postgres.getJdbcUrl());
            TestPropertySourceUtils.addInlinedPropertiesToEnvironment(configurableApplicationContext, "spring.datasource.username=" + postgres.getUsername());
            TestPropertySourceUtils.addInlinedPropertiesToEnvironment(configurableApplicationContext, "spring.datasource.password=" + postgres.getPassword());
        }
    }
}

Conclusion

With TestContainers it is easy and cheap to use a real database for your (integration) tests. I showed you how to use TestContainers with PostgreSQL, but you can use any container. For example in the project I’m currently on where we are using AWS SQS, there is a container which mocks SQS properly. With TestContainers we startup this container simulating SQS to test services producing and consuming from the queue. Thanks for reading :-)

shadow-left