Docker Maven Plugin vs TestContainers – Integration Testing
Why do we need these?
With the advent of containerization, it has become possible to test how our code would behave and interact with each other when placed across containers. One of the most common use-cases is to be able to test database interactions. Although, we will be looking at database interaction example, I have personally and professionally been using this concept for testing service to service interactions as well.
Let’s see how we can create containers using them –
Using Docker Maven Plugin(DMP) – Postgres Container
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.37.0</version>
<executions>
<!-- Build the container in compile phase -->
<execution>
<id>build-docker-container</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
<!-- Start the containers in pre-integration-test phase -->
<execution>
<id>start</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
</execution>
<!-- Stop the containers in post-integration-test phase -->
<execution>
<id>stop</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
<configuration>
<autoCreateCustomNetworks>true</autoCreateCustomNetworks>
<containerNamePattern>%a-%t</containerNamePattern>
<watchInterval>500</watchInterval>
<logDate>default</logDate>
<verbose>true</verbose>
<images>
<!-- pull in a postgres DB image -->
<image>
<alias>pglocal</alias>
<name>postgres:11</name>
<run>
<ports>
<port>${db.port}:5432</port>
</ports>
<env>
<POSTGRES_USER>${db.username}</POSTGRES_USER>
<POSTGRES_PASSWORD>${db.password}</POSTGRES_PASSWORD>
</env>
<wait>
<log>PostgreSQL init process complete</log>
<time>120000</time>
<shutdown>500</shutdown>
</wait>
<log>
<enabled>true</enabled>
</log>
</run>
</image>
</images>
</configuration>
</plugin>
With TestContainers – Redis Container
Below code shows how to use testcontainers to write integration-tests. It uses Jedis which is a java client for Redis.
@Testcontainers
public class TestContainersIT
{
public static final int REDIS_PORT = 6379;
static final DockerImageName REDIS_IMAGE = DockerImageName.parse("redis:7.0.5");
@Container
static final GenericContainer<?> redis = new GenericContainer<>(REDIS_IMAGE).withExposedPorts(REDIS_PORT);
@Test
public void testRedisConnection()
{
try (final Jedis jedis = new Jedis(redis.getHost(), redis.getMappedPort(REDIS_PORT))) {
jedis.connect();
System.out.println("Connected to Redis");
} catch (final Exception ex) {
Assertions.fail();
}
}
}
Code
You can find the code examples for both docker-maven-plugin and testcontainers here
Summary/Opinion
Now that we have seen how we can create containers using both technologies, let us compare a few major differences between the two –
- With test containers, containers can be brought up and down dynamically during the course of execution of tests. With DMP however, all the containers configured in the pom are brought up before the execution of test starts. These containers can be stopped with “post-integration-test” maven phase.
- Using DMP, as is obvious, would require a good knowledge about how maven plugins work, how to pass environment variables etc. TestContainers on the other hand comprise purely of language code such as java etc. There are libraries available in different languages as well.
- Since with DMP, all the container related configuration is present in pom.xml, the demarcation between infrastructure code and test code is quite evident. From clean code perspective, this is a major difference between DMP and test containers. If you’re a lead or an architect then it becomes very easy to keep a tab on what all interactions are being tested looking at the containers involved from pom configuration. In case of test containers, this really is a function of how well the code is maintained.