Testes em JavaScript: Testes Unitários, de Integração e E2E
Testes são uma parte essencial do desenvolvimento de qualquer software de qualidade. No mundo JavaScript (especialmente no ecossistema Node.js), existem três níveis principais de teste: testes unitários, testes de integração e testes E2E (end-to-end). Cada um resolve seus próprios problemas e é usado em diferentes etapas do desenvolvimento. Neste artigo, vamos analisar cada tipo, mostrar exemplos de código e dar recomendações práticas.
1. Testes Unitários
Testes unitários verificam unidades de código individuais e isoladas — funções, métodos ou classes. O principal objetivo é garantir que cada "unidade" funcione corretamente independentemente do resto do sistema. No Node.js, os frameworks mais usados para isso são Jest ou Mocha com Chai.
Exemplo de teste unitário com Jest
Suponha que temos uma função simples para calcular desconto:
// discount.jsfunction calculateDiscount(price, percent) { if (typeof price !== 'number' || typeof percent !== 'number') { throw new Error('Ambos os argumentos devem ser números'); } if (price < 0 || percent < 0 || percent > 100) { throw new Error('Valores inválidos'); } return price - (price * percent / 100);}
module.exports = calculateDiscount;Teste para esta função:
// discount.test.jsconst calculateDiscount = require('./discount');
describe('calculateDiscount', () => { test('calcula corretamente 10% de desconto em 1000', () => { expect(calculateDiscount(1000, 10)).toBe(900); });
test('retorna 0 com 100% de desconto', () => { expect(calculateDiscount(500, 100)).toBe(0); });
test('lança erro com preço negativo', () => { expect(() => calculateDiscount(-100, 10)).toThrow('Valores inválidos'); });
test('lança erro com argumentos não numéricos', () => { expect(() => calculateDiscount('100', 10)).toThrow('Ambos os argumentos devem ser números'); });});- Vantagens: Rápidos, fáceis de escrever, apontam exatamente para o módulo problemático.
- Desvantagens: Não verificam a interação entre componentes.
- Quando usar: Para funções de lógica de negócio, utilitários, validadores.
2. Testes de Integração
Testes de integração verificam como diferentes módulos ou serviços funcionam juntos. Por exemplo, interação com banco de dados, API externa ou sistema de arquivos. No Node.js, para esses testes, frequentemente se usa Supertest (para requisições HTTP) e Jest.
Exemplo de teste de integração para aplicação Express
Suponha que temos um servidor Express simples com um endpoint para criar usuário:
// server.jsconst express = require('express');const app = express();app.use(express.json());
const users = [];
app.post('/users', (req, res) => { const { name, email } = req.body; if (!name || !email) { return res.status(400).json({ error: 'Name and email are required' }); } const user = { id: users.length + 1, name, email }; users.push(user); res.status(201).json(user);});
module.exports = app;Teste de integração com Supertest:
// server.test.jsconst request = require('supertest');const app = require('./server');
describe('POST /users', () => { test('cria um novo usuário com dados válidos', async () => { const response = await request(app) .post('/users') .send({ name: 'João', email: 'joao@example.com' });
expect(response.statusCode).toBe(201); expect(response.body).toHaveProperty('id'); expect(response.bo