How to use Testcontainers for hassle-free integration testing
9 January 2019 |
Paweł Szymański | About a 5 minute read
There are times when “mocking the world” is just not enough.
Database queries, third-party software or some API calls just have to be tested against the real thing. This is especially true when we are working with Cloud, as it provides tools as services with which our applications need to integrate. So what do we do when that happens? We spin up Docker, of course!
Docker is a neat tool that makes life a lot easier in many ways. Recently, I worked on a project that reminded me that it can be simplified even further – thanks to Testcontainers. As a huge TDD fan, I started with writing tests and I thought I’d share a quick, worked example of how Testcontainers can remove some of the hassle of integration testing.
Say Hello to Testcontainers
So what exactly is Testcontainers?
“Testcontainers is a Java 8 library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.” (Source: Github)
What that means is that it is possible (and extremely easy) to spin up a Docker container straight from your integration tests. No scripting required – everything is handled by the test code.
A Worked Example
Imagine you want to write some code that integrates with AWS DynamoDB. There are quite a few ways to run this service – or an equivalent – on your machine:
- Download and run DynamoDB jar file.
- Download and run dynalite.
- Run embedded DynamoDB straight from your Maven project.
- Spin up a Docker container.
Unfortunately, most of these solutions require additional dependencies, like SQLite. On top of that, unlike Docker, they are platform-dependent.
In my example, I will be using the amazon/dynamodb-local Docker image.
To make it clean and easy, our sample application will consist of only two classes. The code is in Kotlin, but could be in Java or Scala. Same thing applies to the tests – I have chosen Spock, but Testcontainers can be employed by anything that runs on JVM.
Product and Products Repository
Our application’s purpose is to save `Product` objects:
To a DynamoDB database:
To run the test, we need to add the following Maven dependency:
When testing database queries, and other things that require an expensive setup, I tend to put the setup code in a common base class. Try not to add any more code to the base class than the minimum necessary for setup and cleanup.
Inheritance in tests can be a code smell.
And finally, our very simple test case:
Notice how I am always creating the table and then deleting it after each test case. This is not really necessary with a single test case, but when more features are tested the database will eventually get polluted. It is better to keep it clean so no accidental dependencies are introduced between the test cases.
Also, keep in mind that the entire container, along with the DynamoDB instance, exists only for the duration of tests and will be discarded afterwards. Consecutive test runs should not depend on each other!
Obviously, the Docker daemon needs to be started before you run your tests. If you forgot to start it, the following exception will be thrown:
java.lang.IllegalStateException: Could not find a valid Docker environment. Please see logs and check configuration
To fix it, simply start Docker.
– Testcontainers provide a degree of flexibility for integration tests that’s often overlooked – make the most of them for more hassle-free integration testing.
– You can start one or multiple containers before each test case or only once per tests execution.
– The library is meant to be used for testing (hence the name). If you are looking for a better way to manage your containers, use orchestration tools such as Kubernetes.
– Be mindful of the environment you’re planning to run your tests in. If it is already dockerized, Testcontainers will not be feasible. Using Docker in Docker is usually a bad idea.
AND Digital Places 23rd In eConsultancy’s Top 100 Digital Agencies Report
AND Digital’s Flagship Learning Conference Returns For Its Third Year
Waterfall Vs Agile: Which Methodology Is Right For Your Organisation?
React Native Engineer (London)
Champion software quality and technical vision for AND and our clients, work on large-scale projects and help junior and mid developers grow in their roles.
Technologies you will be using
Tech Lead (Reading)
Bring your expert tech knowledge to the table to influence the direction of projects, whilst coaching and your team through engineering best practices.I'm Interested
DevOps Lead (Reading)
Bring your delivery expertise to the table, leading the pack as ambassador on operational requirements, influencing and continuous development.I'm Interested