Seed data: populando bancos para desenvolvimento e teste
1. O que é Seed Data e por que ela é essencial
Seed data, ou dados de semente, refere-se ao conjunto inicial de informações inseridas em um banco de dados para simular um ambiente realista durante o desenvolvimento e os testes. Diferentemente de fixtures (dados estáticos usados em testes unitários) ou dados de produção mascarados (cópias anonimizadas de dados reais), a seed data é criada artificialmente para atender necessidades específicas de cada contexto.
A importância da seed data se manifesta em três pilares:
- Consistência entre times: todos os desenvolvedores partem do mesmo estado inicial, eliminando o "funciona na minha máquina"
- Automação de testes: cenários previsíveis permitem validar comportamentos esperados sem depender de dados externos
- Onboarding rápido: novos membros da equipe podem começar a trabalhar imediatamente, sem precisar criar dados manualmente
2. Estratégias de Geração de Seed Data
Existem duas abordagens principais para gerar seed data:
Dados estáticos: arquivos SQL fixos com valores pré-definidos. São previsíveis e versionáveis, mas difíceis de manter em grande escala.
-- seeds/001_usuarios.sql
INSERT INTO usuarios (id, nome, email, criado_em) VALUES
(1, 'Ana Silva', 'ana@exemplo.com', '2024-01-01'),
(2, 'Carlos Souza', 'carlos@exemplo.com', '2024-01-02');
Dados dinâmicos: gerados proceduralmente usando bibliotecas como Faker (Python) ou Faker.js. Produzem dados realistas e volumosos.
-- seeds/gerar_usuarios.py (exemplo conceitual)
from faker import Faker
fake = Faker('pt_BR')
for _ in range(100):
print(f"INSERT INTO usuarios (nome, email) VALUES ('{fake.name()}', '{fake.email()}');")
A recomendação moderna é versionar scripts executáveis (Python, JavaScript, Ruby) em vez de arquivos SQL puros, pois permitem lógica condicional e geração dinâmica.
3. Organização e Estrutura de Seeds no Projeto
Uma estrutura organizada evita confusão e garante reprodutibilidade:
projeto/
├── seeds/
│ ├── dev/
│ │ ├── 001_usuarios.sql
│ │ ├── 002_pedidos.sql
│ │ └── 003_itens_pedido.sql
│ ├── test/
│ │ ├── usuarios_minimos.sql
│ │ └── pedidos_minimos.sql
│ └── common/
│ ├── categorias.sql
│ └── configuracoes.sql
A numeração sequencial (001, 002...) define a ordem de execução, respeitando chaves estrangeiras. Para ciclos (ex.: usuário <-> endereço), use ALTER TABLE ... DISABLE TRIGGER temporariamente ou adie constraints.
4. Técnicas de Inserção Eficiente
Batch inserts: uma única instrução INSERT com múltiplos valores é drasticamente mais rápida que inserts individuais:
-- INEFICIENTE: 1000 inserts separados
INSERT INTO produtos (nome, preco) VALUES ('Produto A', 10.00);
INSERT INTO produtos (nome, preco) VALUES ('Produto B', 20.00);
-- ... 998 inserts
-- EFICIENTE: batch insert
INSERT INTO produtos (nome, preco) VALUES
('Produto A', 10.00),
('Produto B', 20.00),
-- ... até 1000 linhas
('Produto Z', 1000.00);
Resetando identidades: após truncar tabelas, reinicie sequências para evitar conflitos de ID:
TRUNCATE TABLE pedidos, itens_pedido RESTART IDENTITY CASCADE;
Manipulação de sequências: use SETVAL para controlar o próximo valor de uma sequência:
SELECT setval('produtos_id_seq', (SELECT COALESCE(MAX(id), 0) + 1 FROM produtos));
5. Limpeza e Reset de Dados entre Execuções
Três estratégias principais para limpar dados entre execuções:
| Estratégia | Velocidade | Reseta IDs | Mantém estrutura | Recomendado para |
|---|---|---|---|---|
DELETE FROM tabela |
Lenta | Não | Sim | Pequenos volumes |
TRUNCATE tabela |
Rápida | Sim (com RESTART IDENTITY) | Sim | Volumes médios |
| Rollback de transação | Instantânea | Sim | Sim | Testes unitários |
Para testes automatizados, a abordagem mais segura é isolar seeds em uma transação:
BEGIN;
-- Executar seeds aqui
INSERT INTO usuarios (nome, email) VALUES ('Teste', 'teste@exemplo.com');
-- Executar testes...
ROLLBACK; -- Desfaz tudo automaticamente
Scripts de automação podem incluir comandos como:
# seed:clean - remove todos os dados
psql -d meu_banco -c "TRUNCATE SCHEMA public CASCADE;"
# seed:reset - limpa e reinsere
psql -d meu_banco -f seeds/clean.sql
psql -d meu_banco -f seeds/001_usuarios.sql
6. Tratamento de Dados Sensíveis e Mascaramento
Dados pessoais identificáveis (PII) como CPF, e-mail e telefone nunca devem ser commitados. Técnicas de anonimização:
- Substituição: use dados fictícios gerados por Faker
- Hash: aplique hash SHA-256 em campos sensíveis (irreversível)
- Ofuscação: troque caracteres internos (ex.: "joão" → "j***")
-- Exemplo de ofuscação de e-mail
UPDATE usuarios SET email = CONCAT(LEFT(email, 1), '***@', SUBSTRING(email FROM POSITION('@' IN email) + 1));
Boas práticas essenciais:
- Nunca commitar dados reais no repositório
- Usar variáveis de ambiente para senhas e tokens
- Validar seeds em CI/CD para detectar vazamentos acidentais
7. Integração com Ferramentas e Frameworks
Prisma:
// prisma/seed.ts
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
await prisma.usuario.create({
data: { nome: 'Admin', email: 'admin@exemplo.com' }
})
}
main()
ActiveRecord (Ruby on Rails):
# db/seeds.rb
User.create!([
{ name: 'Admin', email: 'admin@exemplo.com' },
{ name: 'Teste', email: 'teste@exemplo.com' }
])
Django ORM:
# seeds.py
from meu_app.models import Usuario
Usuario.objects.create(nome='Admin', email='admin@exemplo.com')
Para bancos NoSQL como MongoDB, seeds são inseridos como documentos JSON:
// seeds/usuarios.json
[
{ "nome": "Ana", "email": "ana@exemplo.com", "ativo": true },
{ "nome": "Carlos", "email": "carlos@exemplo.com", "ativo": false }
]
Em pipelines CI/CD, use variáveis de ambiente para controlar execução:
if [ "$SEED_ENV" = "dev" ]; then
npm run seed:dev
elif [ "$SEED_ENV" = "test" ]; then
npm run seed:test
fi
8. Boas Práticas e Armadilhas Comuns
Armadilha 1: Seeds monolíticos
-- EVITAR: arquivo único de 2000 linhas
-- seeds/tudo.sql contém INSERTs de todas as tabelas
Solução: modularize por domínio (usuários, pedidos, produtos) e use scripts de orquestração.
Armadilha 2: Seeds desatualizados
Quando o schema evolui (novas colunas, constraints), seeds quebram silenciosamente. Mantenha seeds sincronizados com migrations, executando-os como parte do pipeline de CI.
Armadilha 3: Ignorar constraints
-- Isso falha se a tabela categorias estiver vazia
INSERT INTO produtos (nome, categoria_id) VALUES ('Mouse', 999);
Solução: sempre inserir dados pai antes dos filhos, ou usar DEFERRABLE INITIALLY DEFERRED para constraints.
Boas práticas finais:
- Teste seeds em pipeline: valide integridade referencial e constraints
- Use seeds diferentes por ambiente (dev: volumosos; test: mínimos)
- Documente a ordem de execução e dependências entre seeds
Referências
- PostgreSQL Documentation: Populating a Database — Guia oficial sobre técnicas de inserção em lote e otimização de performance no PostgreSQL.
- Prisma Documentation: Seed Database — Tutorial completo sobre como configurar e executar seeds no Prisma ORM.
- Ruby on Rails Guides: Seed Data — Documentação oficial sobre seeds no ActiveRecord, incluindo boas práticas.
- Django Documentation: Providing initial data — Como fornecer dados iniciais com fixtures e data migrations no Django.
- Faker Library Documentation — Biblioteca Python para geração de dados fictícios realistas, essencial para criar seeds dinâmicos.
- MySQL Documentation: INSERT Statement Optimization — Técnicas de otimização de INSERT no MySQL, incluindo batch inserts e manipulação de locks.