Teste de Integração Java com Docker: do Básico às Técnicas Avançadas
O teste de integração é uma etapa criticamente importante no desenvolvimento de aplicações Java modernas, especialmente na arquitetura de microsserviços. O Docker tornou-se o padrão de facto para isolar ambientes de teste, mas simplesmente iniciar um contêiner não é suficiente. No nível avançado, é necessário gerenciar o ciclo de vida dos contêineres, configuração dinâmica de redes, execução paralela de testes e integração perfeita com CI/CD. Neste artigo, abordaremos cenários complexos de teste de integração Java com Docker, usando a biblioteca Testcontainers, e mostraremos como alcançar máxima performance e confiabilidade.
1. Gerenciamento Dinâmico de Contêineres com Testcontainers
Testcontainers é uma biblioteca para Java que permite criar e destruir programaticamente contêineres Docker em testes JUnit. No nível avançado, usamos não apenas os módulos padrão (PostgreSQL, Kafka), mas também contêineres personalizados.
Exemplo: contêiner personalizado para Redis com clusterização
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 disponível em: " + host + ":" + port); // Código aqui para conectar ao Redis e executar operações }}Principais recursos avançados:
- Estratégias de espera (Waiting strategies) — em vez de timeouts fixos, usamos espera por logs, portas ou respostas HTTP.
- Portas dinâmicas — evitamos conflitos de porta durante a execução paralela.
- Comandos personalizados — executamos contêineres com configurações não padrão (por exemplo, Redis em modo cluster).
2. Topologias de Rede Complexas e Docker Compose em Testes
Para testar microsserviços, muitas vezes é necessário levantar vários contêineres interconectados (BD, cache, broker de mensagens). Usar Docker Compose dentro dos testes é uma abordagem avançada que permite descrever toda a infraestrutura em YAML.
Exemplo: integração com Kafka e 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); // Inicialização dos clientes e execução