Integration Testing Java with Docker: Advanced Techniques

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Integration Testing Java with Docker: From Basics to Advanced Techniques



Integration testing is a critically important stage in the development of modern Java applications, especially in a microservices architecture. Docker has become the de facto standard for isolating test environments, but simply running a container is not enough. At an advanced level, container lifecycle management, dynamic network configuration, parallel test execution, and seamless CI/CD integration are required. In this article, we will cover complex scenarios of integration testing Java with Docker using the Testcontainers library and show how to achieve maximum performance and reliability.



1. Dynamic Container Management with Testcontainers



Testcontainers is a Java library that allows you to programmatically create and destroy Docker containers in JUnit tests. At an advanced level, we use not only standard modules (PostgreSQL, Kafka) but also custom containers.



Example: Custom container for Redis with clustering



import org.testcontainers.containers.GenericContainer;import org.testcontainers.containers.wait.strategy.Wait;import org.testcontainers.junit.jupiter.Container;import org.testcontainers.junit.jupiter.Testcontainers;import org.junit.jupiter.api.Test;

import java.time.Duration;

@Testcontainerspublic class RedisClusterTest {

@Container private static GenericContainer<?> redis = new GenericContainer<>("redis:7-alpine") .withExposedPorts(6379) .withCommand("redis-server", "--cluster-enabled", "yes") .waitingFor(Wait.forLogMessage(".*Ready to accept connections.*\\", 1) .withStartupTimeout(Duration.ofSeconds(30)));

@Test void testRedisConnection() { String host = redis.getHost(); Integer port = redis.getMappedPort(6379); System.out.println("Redis available at: " + host + ":" + port); // Code for connecting to Redis and performing operations goes here }}


Key advanced features:

  • Waiting strategies — instead of fixed timeouts, we use waiting for logs, ports, or HTTP responses.
  • Dynamic ports — avoid port conflicts during parallel execution.
  • Custom commands — run containers with non-standard configuration (e.g., Redis in cluster mode).


2. Complex Network Topologies and Docker Compose in Tests



For testing microservices, it is often necessary to spin up several interconnected containers (DB, cache, message broker). Using Docker Compose inside tests is an advanced approach that allows describing the entire infrastructure in YAML.



Example: Integration with Kafka and PostgreSQL via Docker Compose



import org.testcontainers.containers.DockerComposeContainer;import org.testcontainers.junit.jupiter.Container;import org.testcontainers.junit.jupiter.Testcontainers;import org.junit.jupiter.api.Test;

import java.io.File;

@Testcontainerspublic class MicroserviceIntegrationTest {

@Container private static DockerComposeContainer<?> environment = new DockerComposeContainer<>(new File("src/test/resources/docker-compose.yml")) .withExposedService("postgres_1", 5432) .withExposedService("kafka_1", 9092);

@Test void testServiceInteraction() { String pgHost = environment.getServiceHost("postgres_1", 5432); Integer pgPort = environment.getServicePort("postgres_1", 5432); String kafkaHost = environment.getServiceHost("kafka_1", 9092); Integer kafkaPort = environment.getServicePort("kafka_1", 9092); // Initialize clients and execute

Blogs

Book Recommendations