Did you ever struggle while trying to write integration tests without mocking the third party? I know I did.
I’ve known about Martin Fowler since I was a junior developer. He is one of my favorite technology thought-leaders and I enjoy reading the thought-provoking articles that he publishes on his blog. In this post, I want to expand on his article about the test pyramid, in which he explains the difference between unit tests, integration tests and, UI tests. I’ll expand on the part about integration tests.
I want to show you an easy way to run integration tests against a database. With this goal in mind, I’ve created this java demo project. It illustrates the concept and is useful as a starting point for more complicated applications.
In the past, I would delay writing the integration tests. I dislike the idea of not having any integration tests. Fixing errors at runtime is not something that I want to do in Java. Meanwhile, I didn’t want to invest time on setting up a separate test infrastructure. And tests running against a mocked database, are not testing anything. The options seemed limited:
- mocking the database interaction layer
- connecting to an in-memory database
- connecting to a local/remote test database
Regardless of the option I picked, I always had the feeling that the solution could be more elegant. My hesitation would turn into frustration as the application grew and the tests would become harder to maintain. Recently, I discovered a better option: connecting the tests to a database running in a Docker container.
In my last project, Vlad and Max showed me that using docker containers simplifies running integration tests against a database. No mocking, no complicated infrastructure, no tinkering with configuration files. As long as you can run Docker on the build machine, you can run the tests.
This is the elegant solution that I was looking for:
- install and configure Docker on the build servers and the developer machines that will run the tests
- use docker-maven-plugin to build a docker image of the database and start/stop a container based on the image
- populate the database with the necessary data (use ‘/docker-entrypoint-initdb.d/’ or a database migration tool like flyway)
- finally, use maven-failsafe-plugin to run the integration tests
I like this approach because the integration tests are portable and easy to debug. If the tests run on my laptop, they will run also on the build machine. If the tests fail on the build machine, the problem will also appear on my laptop.
I also like that the tests are repeatable: the plugin builds the Docker image from scratch before the tests start and removes it after the tests finish.
Finally, I like that this approach is transferable to other types of integration tests. It’s easy to replace the database container with a dockerized REST API (in a move towards contract testing) or with a message queue container.
The demo project is more than a typical “hello world” application. The integration test starts a docker MySQL container and makes a simple SQL query. You will notice that the docker-maven-plugin configuration is more complicated than necessary, given such a simple test. The reason: the code samples that I found online seemed trivial. I wanted to have a “bootstrap” project that I could reuse in real-life projects without the hassle of gluing everything from scratch.
This approach to writing integration tests has opened my eyes to the possibilities that exist today in the QA automation domain. The tools built on top of Docker seem varied enough to accommodate all test types. The number of plausible excuses for not having a proper CI pipeline is getting too low…