Testcontainers na Prática: Testes de Integração com Docker para Microsserviços Modernos

Por que Testes de Integração Ainda São um Desafio?
Mocks e bancos em memória são soluções tradicionais para testes de integração, mas trazem uma fragilidade silenciosa: o comportamento do H2 não é idêntico ao do PostgreSQL, um mock de Redis não valida comandos reais, e um Fake S3 não reproduz as nuances de latência e consistência do serviço real. Bugs que passam nesses ambientes artificiais frequentemente explodem em produção.
O Testcontainers resolve esse problema de forma elegante: ele gerencia containers Docker reais dentro do ciclo de vida dos seus testes. Cada execução ganha um ambiente isolado, idêntico ao de produção, que é destruído automaticamente ao final.
Como o Testcontainers Funciona
Disponível para Java, Python, Go, Node.js e outras linguagens, o Testcontainers utiliza a API do Docker para:
- Criar containers a partir de imagens Docker reais (postgres:16-alpine, redis:7, confluentinc/cp-kafka)
- Configurar variáveis de ambiente, volumes, redes e portas dinâmicas
- Executar scripts de inicialização (SQL de schema, seed data) antes dos testes
- Aguardar prontidão com health checks customizados
- Destruir tudo ao final dos testes — sem resíduos, sem estado compartilhado
Exemplo com Java, Spring Boot e PostgreSQL
@SpringBootTest
@Testcontainers
class UsuarioRepositoryTest {
@Container
static PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:16-alpine")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@DynamicPropertySource
static void properties(DynamicPropertyRegistry r) {
r.add("spring.datasource.url", postgres::getJdbcUrl);
r.add("spring.datasource.username", postgres::getUsername);
r.add("spring.datasource.password", postgres::getPassword);
}
@Autowired
UsuarioRepository repository;
@Test
void buscaPorEmail() {
repository.save(new Usuario("Maria", "maria@email.com"));
var result = repository.findByEmail("maria@email.com");
assertThat(result).isPresent();
assertThat(result.get().getNome()).isEqualTo("Maria");
}
}
Anotações-chave: @Testcontainers gerencia o ciclo de vida, @Container declara o container PostgreSQL, e @DynamicPropertySource injeta as credenciais dinâmicas no Spring. Quando o teste termina, o container é removido automaticamente pelo Ryuk.
Exemplo com Python, Redis e Testcontainers
from testcontainers.redis import RedisContainer
import redis
def test_cache_com_redis_real():
with RedisContainer("redis:7-alpine") as redis_container:
host = redis_container.get_container_host_ip()
port = redis_container.get_exposed_port(6379)
client = redis.Redis(host=host, port=port)
client.set("sessao:123", "ativo")
assert client.get("sessao:123") == b"ativo"
# Container é destruído ao sair do 'with'
Módulos que Valem Ouro
- LocalStack: emula S3, SQS, DynamoDB, Lambda — essencial para testar integrações AWS localmente
- Kafka / Redpanda: validade producer/consumer sem cluster externo
- Toxiproxy: simula latência e falhas de rede para testar resiliência
- MongoDB: teste agregações complexas com o banco real, não com o em-memory
- Elasticsearch: valide mappings e queries de busca full-text
Boas Práticas em Produção
- Prefira Alpine: imagens -alpine reduzem download e footprint — cruciais em CI paralelo
- Portas dinâmicas sempre: evite conflitos entre execuções concorrentes
- Compartilhe com cuidado: containers podem ser compartilhados dentro da mesma classe via static, nunca entre classes sem limpeza
- Pré-baixe imagens no CI: acrescente docker pull no setup do pipeline para evitar timeouts
- Não desabilite o Ryuk: o container Ryuk garante limpeza mesmo se os testes forem abruptamente interrompidos
Testcontainers em Pipelines CI/CD
No GitHub Actions, GitLab CI ou CircleCI, o Docker já está disponível. Configure o serviço Docker e os testes rodam sem modificações:
# .github/workflows/ci.yml
jobs:
test:
runs-on: ubuntu-latest
services:
docker:
image: docker:27-dind
options: --privileged
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '21'
- run: ./mvnw test
# Testcontainers gerencia tudo automaticamente
Conclusão
Testcontainers eleva a confiabilidade dos testes de integração ao eliminar o abismo entre o ambiente de teste e o de produção. Com containers reais, descartáveis e programáticos, você ganha testes mais rápidos de escrever, mais confiáveis na execução e que pegam bugs que mocks jamais pegariam. Adote em um módulo hoje e sinta a diferença.







