Como usar Docker para testes locais rápidos
1. Por que Docker é ideal para testes locais rápidos
O Docker transforma a experiência de testes locais ao oferecer isolamento completo de dependências e ambientes perfeitamente replicáveis. Em vez de instalar bancos de dados, filas de mensagens e outras ferramentas diretamente no sistema operacional do desenvolvedor, cada componente roda em seu próprio container, garantindo que o ambiente de teste seja idêntico ao de produção.
O problema clássico "funciona na minha máquina" desaparece porque o Docker empacota não apenas o código, mas também todas as bibliotecas, versões de runtime e configurações necessárias. Além disso, o ciclo de build, teste e destruição é extremamente rápido: containers podem ser criados, executados e removidos em segundos, sem deixar resíduos no sistema hospedeiro. Essa natureza stateless permite que cada execução de teste comece com um ambiente limpo e previsível.
2. Configurando um ambiente de teste mínimo com Docker Compose
O Docker Compose é a ferramenta ideal para orquestrar múltiplos containers que simulam o ambiente completo de testes. Um arquivo docker-compose.yml básico para testes pode incluir serviços como banco de dados, cache e a própria aplicação:
version: '3.8'
services:
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U testuser -d testdb"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
app:
build:
context: .
dockerfile: Dockerfile.test
environment:
DATABASE_URL: postgresql://testuser:testpass@db:5432/testdb
REDIS_URL: redis://redis:6379/0
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
volumes:
- .:/app
command: ["npm", "test"]
Variáveis de ambiente tornam a configuração flexível, permitindo que o mesmo arquivo funcione em diferentes contextos (CI, desenvolvimento local, produção) apenas alterando os valores externamente.
3. Executando testes unitários dentro do container
Um Dockerfile otimizado para testes utiliza construção em múltiplos estágios (multistage build) para separar dependências de desenvolvimento das de produção:
# Estágio 1: Instalação de dependências
FROM node:18-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=development
# Estágio 2: Testes
FROM node:18-alpine AS test
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
CMD ["npm", "test"]
Para rodar os testes sem modificar o ambiente local, execute:
docker compose up --build --abort-on-container-exit
A montagem de volumes (volumes: - .:/app) permite hot-reload: alterações no código são refletidas imediatamente dentro do container, sem necessidade de reconstruir a imagem a cada modificação.
4. Testes de integração com dependências externas
Testes de integração exigem que serviços como PostgreSQL e Redis estejam disponíveis e saudáveis. O uso de depends_on com condition: service_healthy garante que o container da aplicação só inicie quando o banco estiver pronto para receber conexões:
services:
app:
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
Para simular APIs externas, utilize ferramentas como WireMock ou MockServer em containers separados. A limpeza automática de dados entre execuções pode ser feita com volumes temporários:
services:
db:
volumes:
- test_db_data:/var/lib/postgresql/data
volumes:
test_db_data:
Cada execução de docker compose down -v remove os volumes, garantindo um estado inicial limpo.
5. Estratégias para testes de aceitação e E2E
Testes de aceitação e ponta a ponta (E2E) se beneficiam de containers temporários que simulam o ambiente completo de produção. Use redes Docker para isolar a comunicação entre serviços:
networks:
test_network:
driver: bridge
services:
app:
networks:
- test_network
cypress:
image: cypress/included:latest
networks:
- test_network
environment:
CYPRESS_BASE_URL: http://app:3000
volumes:
- ./cypress:/e2e
- ./cypress/reports:/reports
Dentro do container de testes E2E, é possível gerar relatórios e screenshots:
command: >
sh -c "cypress run --spec 'cypress/e2e/**/*.cy.js' --reporter junit --reporter-options 'mochaFile=/reports/test-results.xml'"
6. Dicas para acelerar o ciclo de teste
O cache de camadas Docker é uma das maiores vantagens para acelerar builds. Organize o Dockerfile para que as instruções que mudam com menos frequência (instalação de dependências) fiquem no início:
COPY package*.json ./
RUN npm ci
COPY . .
Use --build-arg para parametrizar versões de dependências sem invalidar o cache:
docker compose build --build-arg NODE_VERSION=18
Compartilhe imagens base entre projetos usando um registry privado ou imagens públicas otimizadas. Automatize comandos frequentes com scripts de shell:
#!/bin/bash
# test.sh
docker compose up -d db redis
sleep 2
docker compose run --rm app npm run test:integration
docker compose down
7. Tratamento de falhas e depuração em testes
Quando um teste falha, a depuração eficiente é crucial. Use docker logs para visualizar logs centralizados:
docker compose logs app
Para inspecionar o estado de um container sem encerrá-lo, utilize docker pause:
docker compose pause app
# Inspecione o container com docker exec -it app sh
docker compose unpause app
Snapshots de estado podem ser criados com volumes nomeados. Antes de um teste crítico, faça backup do volume:
docker run --rm -v test_db_data:/source -v $(pwd):/backup alpine tar czf /backup/db_snapshot.tar.gz -C /source .
Para restaurar:
docker run --rm -v test_db_data:/target -v $(pwd):/backup alpine tar xzf /backup/db_snapshot.tar.gz -C /target
8. Boas práticas e armadilhas comuns
Evite expor portas desnecessárias durante os testes. Em vez de mapear portas para o host, utilize a rede interna do Docker:
services:
db:
expose:
- "5432"
# Sem ports: para testes locais
Gerencie recursos de CPU e memória para evitar que containers de teste consumam todo o sistema:
services:
app:
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
Versionamento de imagens é essencial para rastreabilidade. Use tags semânticas:
docker build -t myapp-test:1.2.3 .
Para variações locais sem modificar o docker-compose.yml principal, utilize docker-compose.override.yml. Este arquivo é automaticamente mesclado e permite ajustes como montagem de volumes extras ou variáveis de ambiente específicas do desenvolvedor.
Referências
- Documentação Oficial do Docker Compose — Guia completo sobre orquestração de containers com Docker Compose, incluindo exemplos de configuração para ambientes de teste.
- Dockerfile Best Practices — Recomendações oficiais para escrever Dockerfiles eficientes, com foco em cache de camadas e construção em múltiplos estágios.
- Testing Strategies with Docker — Artigo técnico sobre estratégias de teste utilizando containers, abordando testes unitários, integração e E2E.
- Cypress Docker Documentation — Guia oficial para execução de testes E2E com Cypress em containers Docker, incluindo configuração de redes e geração de relatórios.
- PostgreSQL Docker Healthcheck — Exemplos de healthchecks para PostgreSQL em containers, essenciais para sincronização de dependências em testes de integração.
- WireMock Docker Image — Documentação do WireMock para simulação de APIs em containers, útil para testes de integração com serviços externos.
- Docker Resource Constraints — Guia oficial sobre limitação de CPU e memória em containers, importante para evitar sobrecarga durante testes locais.