8 min leitura • Guide 351 of 877
Estratégias de Automação de Testes
Boa automação de testes captura bugs antes dos usuários. Má automação de testes é lenta, flaky e é ignorada. O objetivo é construir automação que equipes confiam e mantêm. Este guia cobre abordagens práticas para automação de testes.
Pirâmide de Testes
| Nível | Velocidade | Custo | Cobertura |
|---|---|---|---|
| Unitário | Rápido | Baixo | Muitos |
| Integração | Médio | Médio | Alguns |
| E2E | Lento | Alto | Poucos |
Tipos de Teste
Diferentes Níveis de Teste
PIRÂMIDE DE TESTES
══════════════════
▲
/E\ Testes E2E
/2E \ (poucos, lentos)
/─────\
/ \
/ Integr- \ Testes de Integração
/ ação \ (alguns, médios)
/─────────────\
/ \
/ Testes Unit. \ Testes Unitários
/ \ (muitos, rápidos)
/─────────────────────\
TESTES UNITÁRIOS:
─────────────────────────────────────
Propósito:
├── Testar funções/classes individuais
├── Isolado de dependências
├── Execução rápida
├── Muitos testes
├── Fundação da automação
└── Maioria dos testes
Características:
├── Rodam em milissegundos
├── Sem dependências externas
├── Sem banco, sem rede
├── Mock de chamadas externas
├── Rodam frequentemente
└── Owned por desenvolvedores
TESTES DE INTEGRAÇÃO:
─────────────────────────────────────
Propósito:
├── Testar interações entre componentes
├── Banco real (instância de teste)
├── Chamadas API reais (para env de teste)
├── Verificar que integrações funcionam
└── Cobertura média
Características:
├── Rodam em segundos
├── Algumas dependências externas
├── Banco de teste
├── Mais realista
├── Rodam no CI
└── Caminhos críticos
TESTES E2E:
─────────────────────────────────────
Propósito:
├── Testar fluxos completos do usuário
├── Browser/app real
├── Verificação end-to-end
├── Apenas jornadas principais
└── Maior confiança
Características:
├── Rodam em minutos
├── Ambiente completo
├── Mais lentos, mais caros
├── Mais flaky (mais partes móveis)
├── Rodam antes de release
└── Cobertura seletiva
Testes Unitários
Fundação da Automação
BOAS PRÁTICAS DE TESTE UNITÁRIO
═══════════════════════════════
BOM TESTE UNITÁRIO:
─────────────────────────────────────
Características:
├── Rápido (< 100ms)
├── Isolado (sem deps externas)
├── Determinístico (mesmo resultado)
├── Focado (testa uma coisa)
├── Legível (documentação)
└── Manutenível
Exemplo:
describe('calcularTotal', () => {
it('soma preços dos itens', () => {
const items = [
{ preco: 10, quantidade: 2 },
{ preco: 5, quantidade: 1 }
];
expect(calcularTotal(items)).toBe(25);
});
it('retorna 0 para lista vazia', () => {
expect(calcularTotal([])).toBe(0);
});
it('lança erro para item sem preço', () => {
const items = [{ quantidade: 1 }];
expect(() => calcularTotal(items))
.toThrow('Item sem preço');
});
});
─────────────────────────────────────
O QUE TESTAR:
├── Happy path
├── Edge cases
├── Erro handling
├── Boundary conditions
└── Null/undefined
Padrão AAA
PADRÃO ARRANGE-ACT-ASSERT:
┌─────────────────────────────────────────────────────────────┐
│ │
│ it('aplica desconto corretamente', () => { │
│ // ARRANGE (Preparar) │
│ const carrinho = criarCarrinho(); │
│ carrinho.adicionarItem({ preco: 100 }); │
│ const cupom = { tipo: 'percentual', valor: 10 }; │
│ │
│ // ACT (Agir) │
│ carrinho.aplicarCupom(cupom); │
│ │
│ // ASSERT (Verificar) │
│ expect(carrinho.total).toBe(90); │
│ }); │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ REGRAS: │
│ • Uma responsabilidade por teste │
│ • Nome descritivo do cenário │
│ • Setup claro e mínimo │
│ • Uma asserção principal │
│ • Teste falha por uma razão │
└─────────────────────────────────────────────────────────────┘
Testes de Integração
Testando Componentes Juntos
TESTES DE INTEGRAÇÃO:
┌─────────────────────────────────────────────────────────────┐
│ │
│ QUANDO USAR: │
│ ───────────── │
│ • API endpoints │
│ • Interação com banco de dados │
│ • Comunicação entre serviços │
│ • Fluxos de negócio críticos │
│ │
│ EXEMPLO - TESTE DE API: │
│ │
│ describe('POST /api/usuarios', () => { │
│ beforeEach(async () => { │
│ await limparBanco(); │
│ }); │
│ │
│ it('cria usuário com dados válidos', async () => { │
│ const response = await request(app) │
│ .post('/api/usuarios') │
│ .send({ │
│ email: 'teste@email.com', │
│ nome: 'Teste' │
│ }); │
│ │
│ expect(response.status).toBe(201); │
│ expect(response.body.id).toBeDefined(); │
│ │
│ // Verifica no banco │
│ const usuario = await Usuario.findById( │
│ response.body.id │
│ ); │
│ expect(usuario.email).toBe('teste@email.com'); │
│ }); │
│ │
│ it('rejeita email duplicado', async () => { │
│ await Usuario.create({ │
│ email: 'existente@email.com' │
│ }); │
│ │
│ const response = await request(app) │
│ .post('/api/usuarios') │
│ .send({ email: 'existente@email.com' }); │
│ │
│ expect(response.status).toBe(409); │
│ }); │
│ }); │
└─────────────────────────────────────────────────────────────┘
Testes E2E
Fluxos Completos
TESTES END-TO-END:
┌─────────────────────────────────────────────────────────────┐
│ │
│ QUANDO USAR: │
│ ───────────── │
│ • Fluxos críticos de usuário │
│ • Jornadas de conversão │
│ • Smoke tests antes de release │
│ • Poucos testes, alto valor │
│ │
│ O QUE TESTAR (seletivo): │
│ • Login/Signup │
│ • Fluxo de compra │
│ • Funcionalidades core │
│ • Happy paths principais │
│ │
│ EXEMPLO - CYPRESS: │
│ │
│ describe('Fluxo de Compra', () => { │
│ it('usuário completa compra', () => { │
│ // Login │
│ cy.visit('/login'); │
│ cy.get('[data-testid=email]').type('user@test.com');│
│ cy.get('[data-testid=senha]').type('senha123'); │
│ cy.get('[data-testid=submit]').click(); │
│ │
│ // Adicionar ao carrinho │
│ cy.visit('/produtos/123'); │
│ cy.get('[data-testid=add-carrinho]').click(); │
│ │
│ // Checkout │
│ cy.get('[data-testid=ir-checkout]').click(); │
│ cy.get('[data-testid=confirmar]').click(); │
│ │
│ // Verificar │
│ cy.url().should('include', '/confirmacao'); │
│ cy.contains('Pedido confirmado'); │
│ }); │
│ }); │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ DICAS PARA E2E: │
│ • Use data-testid para seletores (não classes CSS) │
│ • Waits explícitos, não implícitos │
│ • Teste em ambiente isolado │
│ • Dados de teste limpos │
│ • Poucos testes, bem mantidos │
└─────────────────────────────────────────────────────────────┘
Lidando com Testes Flaky
Eliminando Instabilidade
COMBATENDO TESTES FLAKY:
┌─────────────────────────────────────────────────────────────┐
│ │
│ CAUSAS COMUNS: │
│ ────────────── │
│ │
│ ❌ Dependência de timing │
│ ❌ Estado compartilhado entre testes │
│ ❌ Dependência de ordem de execução │
│ ❌ Race conditions │
│ ❌ Dados de teste não limpos │
│ ❌ Ambiente instável │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ SOLUÇÕES: │
│ ────────── │
│ │
│ TIMING: │
│ ❌ await sleep(1000); │
│ ✅ await waitFor(() => expect(element).toBeVisible()); │
│ │
│ ISOLAMENTO: │
│ ❌ Testes compartilham dados │
│ ✅ Cada teste cria seus dados, limpa depois │
│ │
│ DETERMINISMO: │
│ ❌ const id = Date.now(); │
│ ✅ const id = 'test-id-123'; │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ POLÍTICA DE FLAKY: │
│ ───────────────── │
│ │
│ 1. Teste falhou aleatoriamente? QUARENTENA │
│ 2. Investigar causa raiz │
│ 3. Corrigir ou deletar (não ignorar) │
│ 4. Nunca merge com teste flaky │
│ │
│ Testes flaky não testados = pior que não ter testes │
└─────────────────────────────────────────────────────────────┘
Estratégia de Cobertura
O Que Cobrir
ESTRATÉGIA DE COBERTURA:
┌─────────────────────────────────────────────────────────────┐
│ │
│ PRIORIDADES: │
│ ──────────── │
│ │
│ 1. CÓDIGO CRÍTICO DE NEGÓCIO │
│ • Cálculos financeiros: 100% │
│ • Lógica de autorização: 100% │
│ • Fluxos de pagamento: 100% │
│ │
│ 2. CÓDIGO COMPLEXO │
│ • Algoritmos: Alta cobertura │
│ • State machines: Alta cobertura │
│ • Parsing/transformação: Alta cobertura │
│ │
│ 3. CÓDIGO COMUM │
│ • CRUD simples: Cobertura moderada │
│ • UI: Cobertura baixa (testes E2E cobrem) │
│ • Glue code: Cobertura baixa │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ METAS DE COBERTURA: │
│ │
│ NÃO: "80% de cobertura em tudo" │
│ │
│ SIM: "100% em paths críticos, │
│ 70% em lógica de negócio, │
│ 50% overall" │
│ │
│ Cobertura é métrica de input, não de qualidade │
│ Código coberto ≠ código bem testado │
└─────────────────────────────────────────────────────────────┘