Por que Testes sao Importantes
Testes sao um meio importante para garantir a qualidade do codigo e prevenir regressoes (bugs de regressao).
Codigo sem testes:
- Refatoracao assustadora
- Impacto das mudancas desconhecido
- Bugs descobertos em producao
Piramide de Testes
Um modelo que mostra os tipos de teste e a proporcao recomendada.
flowchart TB
subgraph Pyramid["Piramide de Testes"]
E2E["Testes E2E<br/>Poucos (10%)<br/>Alto custo, lentos, instaveis"]
Integration["Testes de Integracao<br/>Moderados (20%)<br/>Custo medio, velocidade media"]
Unit["Testes Unitarios<br/>Muitos (70%)<br/>Baixo custo, rapidos, estaveis"]
E2E --> Integration --> Unit
end
Testes Unitarios
Testam funcoes ou classes individualmente de forma isolada.
Caracteristicas
| Item | Teste Unitario |
|---|---|
| Alvo | Funcoes, classes, modulos |
| Velocidade | Muito rapido (milissegundos) |
| Estabilidade | Alta |
| Cobertura | Estreita (funcionalidade unica) |
Exemplo de Implementacao
// Codigo a ser testado
function calculateTotal(items, taxRate) {
const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
return Math.round(subtotal * (1 + taxRate));
}
// Teste
describe('calculateTotal', () => {
it('calcula o valor total dos produtos', () => {
const items = [
{ price: 100, quantity: 2 },
{ price: 200, quantity: 1 }
];
expect(calculateTotal(items, 0.1)).toBe(440);
});
it('retorna 0 para array vazio', () => {
expect(calculateTotal([], 0.1)).toBe(0);
});
it('retorna subtotal quando taxa e 0%', () => {
const items = [{ price: 100, quantity: 1 }];
expect(calculateTotal(items, 0)).toBe(100);
});
});
Padrao AAA
it('cria um usuario', () => {
// Arrange (Preparar)
const userData = { name: 'Alice', email: 'alice@example.com' };
// Act (Agir)
const user = createUser(userData);
// Assert (Verificar)
expect(user.id).toBeDefined();
expect(user.name).toBe('Alice');
});
Testes de Integracao
Testam se multiplos componentes funcionam juntos corretamente.
Caracteristicas
| Item | Teste de Integracao |
|---|---|
| Alvo | API, integracao com banco de dados, servicos externos |
| Velocidade | Moderada (segundos) |
| Estabilidade | Moderada |
| Cobertura | Moderada |
Exemplo de Implementacao (API)
describe('POST /api/users', () => {
beforeEach(async () => {
await db.users.deleteMany();
});
it('cria um novo usuario', async () => {
const response = await request(app)
.post('/api/users')
.send({ name: 'Alice', email: 'alice@example.com' })
.expect(201);
expect(response.body.id).toBeDefined();
expect(response.body.name).toBe('Alice');
// Confirma que foi salvo no banco de dados
const user = await db.users.findById(response.body.id);
expect(user).not.toBeNull();
});
it('retorna erro para email duplicado', async () => {
await db.users.create({ name: 'Bob', email: 'alice@example.com' });
await request(app)
.post('/api/users')
.send({ name: 'Alice', email: 'alice@example.com' })
.expect(409);
});
});
Testes E2E (End-to-End)
Testam a aplicacao inteira do ponto de vista do usuario.
Caracteristicas
| Item | Teste E2E |
|---|---|
| Alvo | Fluxo completo do usuario |
| Velocidade | Lento (minutos) |
| Estabilidade | Baixa (propenso a flakiness) |
| Cobertura | Ampla |
Exemplo de Implementacao (Playwright)
import { test, expect } from '@playwright/test';
test('faz login e exibe dashboard', async ({ page }) => {
// Navega para pagina de login
await page.goto('/login');
// Preenche formulario
await page.fill('[name="email"]', 'user@example.com');
await page.fill('[name="password"]', 'password123');
await page.click('button[type="submit"]');
// Confirma redirecionamento para dashboard
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('h1')).toContainText('Dashboard');
});
test('busca produto e compra', async ({ page }) => {
await page.goto('/');
// Busca
await page.fill('[name="search"]', 'Notebook');
await page.click('button[type="submit"]');
// Adiciona produto ao carrinho
await page.click('[data-testid="add-to-cart"]');
// Vai para o carrinho
await page.click('[data-testid="cart-icon"]');
await expect(page.locator('.cart-item')).toHaveCount(1);
});
Mock/Stub
Simula dependencias externas para isolar os testes.
// Mock de API externa
jest.mock('./paymentService', () => ({
processPayment: jest.fn().mockResolvedValue({ success: true, transactionId: 'tx_123' })
}));
import { processPayment } from './paymentService';
it('processa pagamento', async () => {
const result = await checkout(order);
expect(processPayment).toHaveBeenCalledWith({
amount: order.total,
currency: 'BRL'
});
expect(result.transactionId).toBe('tx_123');
});
Desenvolvimento Orientado a Testes (TDD)
Metodologia de desenvolvimento onde se escreve o teste antes da implementacao.
flowchart LR
Red["Red<br/>Escrever teste que falha"] --> Green["Green<br/>Implementacao minima para passar"] --> Refactor["Refactor<br/>Melhorar codigo"]
Refactor --> Red
Exemplo de TDD
// 1. Red: Escrever teste que falha
it('erro quando senha tem menos de 8 caracteres', () => {
expect(() => validatePassword('1234567')).toThrow('Password too short');
});
// 2. Green: Implementacao minima
function validatePassword(password) {
if (password.length < 8) {
throw new Error('Password too short');
}
}
// 3. Refactor: Melhorar codigo conforme necessario
Cobertura
Indicador que mostra quanto do codigo e coberto pelos testes.
| Tipo de Cobertura | Descricao |
|---|---|
| Cobertura de Linha | Porcentagem de linhas executadas |
| Cobertura de Branch | Porcentagem de ramificacoes executadas |
| Cobertura de Funcao | Porcentagem de funcoes executadas |
Meta de Cobertura
80% e uma meta comum
Porem:
- Nao e necessario buscar 100%
- Alta cobertura != alta qualidade de testes
- Priorize cobrir caminhos importantes
Como Escolher a Estrategia de Testes
| Cenario | Teste a Priorizar |
|---|---|
| Logica de negocio complexa | Testes Unitarios |
| Muitas integracoes externas | Testes de Integracao |
| UI importante | Testes E2E |
| Manutencao de codigo legado | E2E para criar rede de seguranca |
Resumo
Uma estrategia de testes eficaz e aquela que, consciente da piramide de testes, distribui equilibradamente os testes em cada nivel. Centralize nos testes unitarios, confirme integracoes com testes de integracao e garanta fluxos importantes do usuario com testes E2E. Testes nao sao algo que reduz a velocidade de desenvolvimento, mas sim um investimento que melhora a qualidade e eficiencia de desenvolvimento a longo prazo.
← Voltar para a lista