Testes de Integração JavaScript com Docker: Guia Completo
Testes de integração são uma etapa crítica no desenvolvimento JavaScript (Node.js). Eles verificam a interação da sua aplicação com serviços externos: bancos de dados, APIs, caches e filas. Usar Docker para esses fins permite isolar o ambiente de teste, garantir sua reprodutibilidade e evitar problemas de "funciona na minha máquina". Neste artigo, vamos abordar como configurar testes de integração para aplicações JavaScript usando Docker, Docker Compose e frameworks de teste populares.
Por que usar Docker para Testes de Integração?
Testes de integração tradicionais frequentemente enfrentam problemas: dependências podem diferir em diferentes máquinas de desenvolvedores, bancos de dados podem estar em estados diferentes, e a configuração do ambiente leva horas. O Docker resolve esses problemas fornecendo:
- Isolamento: cada execução de teste ocorre em um contêiner limpo.
- Reprodutibilidade: ambiente idêntico para todos os desenvolvedores e CI/CD.
- Velocidade: contêineres iniciam em segundos, não minutos.
- Gerenciamento de dependências: fácil subir PostgreSQL, Redis, RabbitMQ e outros serviços.
Configuração do Ambiente de Teste com Docker Compose
Vamos começar criando o projeto. Suponha que temos uma aplicação Node.js simples que salva dados no PostgreSQL. Criaremos o arquivo docker-compose.test.yml:
version: '3.8'
services: test-runner: build: context: . dockerfile: Dockerfile.test depends_on: - db - redis environment: - NODE_ENV=test - DATABASE_URL=postgresql://testuser:testpass@db:5432/testdb - REDIS_URL=redis://redis:6379 volumes: - ./coverage:/app/coverage
db: image: postgres:15-alpine environment: POSTGRES_USER: testuser POSTGRES_PASSWORD: testpass POSTGRES_DB: testdb ports: - "5432" tmpfs: - /var/lib/postgresql/data
redis: image: redis:7-alpine ports: - "6379"Observe o tmpfs para o PostgreSQL — isso garante que após a conclusão dos testes os dados não sejam salvos, e cada execução começará com um banco limpo.
Dockerfile para Testes
Criaremos o Dockerfile.test, que é otimizado para testes, não para produção:
FROM node:18-alpine
WORKDIR /app
# Copiamos apenas package.json para cache da camada de dependênciasCOPY package*.json ./RUN npm ci --only=production && npm ci --only=dev
# Copiamos o código fonteCOPY . .
# Instalamos wait-for-it para aguardar a prontidão dos serviçosADD https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh /wait-for-it.shRUN chmod +x /wait-for-it.sh
CMD ["sh", "-c", "/wait-for-it.sh db:5432 -- /wait-for-it.sh redis:6379 -- npm test"]Escrevendo Testes de Integração em JavaScript
Agora vamos escrever os próprios testes. Usaremos Jest e Supertest para testar a API. Criaremos o arquivo tests/integration/user.test.js:
const request = require('supertest');const { app } = require('../src/app');const { Pool } = require('pg');
describe('User API Integration Tests', () => { let pool;
beforeAll(async () => { // Criamos um pool de conexões para o banco de teste pool = new Pool({ connectionString: process.env.DATABASE_URL });
// Executamos migrações antes dos testes const migrationSQL = ` CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(255) UNIQUE NOT NULL, created_at TIMESTAMP DEFAULT NOW() ); `; await pool.query(migrationSQL); });
afterAll(as