Testes de Integração
Testes de integração verificam se os componentes funcionam juntos corretamente.
Setup com Supertest
bash
npm install -D supertest @types/supertestBanco de Teste
typescript
// src/tests/setup.tsimport { prisma } from '../config/database'; beforeAll(async () => { // Limpa banco antes dos testes await prisma.$executeRaw`TRUNCATE TABLE "User", "Product", "Order" CASCADE`;}); afterAll(async () => { await prisma.$disconnect();});javascript
// jest.config.jsmodule.exports = { // ... outras configs setupFilesAfterEnv: ['<rootDir>/src/tests/setup.ts'], testTimeout: 30000,};Testando Rotas
typescript
// src/tests/integration/auth.test.tsimport request from 'supertest';import { app } from '../../app';import { prisma } from '../../config/database'; describe('Auth Routes', () => { const userData = { name: 'Test User', email: 'test@example.com', password: 'password123', }; beforeEach(async () => { await prisma.user.deleteMany(); }); describe('POST /api/auth/register', () => { it('deve registrar novo usuário', async () => { const response = await request(app) .post('/api/auth/register') .send(userData); expect(response.status).toBe(201); expect(response.body).toHaveProperty('user'); expect(response.body.user.email).toBe(userData.email); expect(response.body).toHaveProperty('token'); }); it('deve retornar erro se email já existe', async () => { // Primeiro registro await request(app).post('/api/auth/register').send(userData); // Segundo registro com mesmo email const response = await request(app) .post('/api/auth/register') .send(userData); expect(response.status).toBe(400); expect(response.body.error).toBe('E-mail já cadastrado'); }); it('deve validar campos obrigatórios', async () => { const response = await request(app) .post('/api/auth/register') .send({ email: 'test@example.com' }); expect(response.status).toBe(400); expect(response.body).toHaveProperty('errors'); }); }); describe('POST /api/auth/login', () => { beforeEach(async () => { await request(app).post('/api/auth/register').send(userData); }); it('deve fazer login com credenciais válidas', async () => { const response = await request(app) .post('/api/auth/login') .send({ email: userData.email, password: userData.password, }); expect(response.status).toBe(200); expect(response.body).toHaveProperty('token'); }); it('deve rejeitar senha incorreta', async () => { const response = await request(app) .post('/api/auth/login') .send({ email: userData.email, password: 'wrongpassword', }); expect(response.status).toBe(401); }); });});Helpers de Teste
typescript
// src/tests/helpers.tsimport request from 'supertest';import { app } from '../app';import { prisma } from '../config/database'; export async function createTestUser(data = {}) { const userData = { name: 'Test User', email: `test-${Date.now()}@example.com`, password: 'password123', ...data, }; const response = await request(app) .post('/api/auth/register') .send(userData); return { user: response.body.user, token: response.body.token, };} export async function createTestProduct(token: string, data = {}) { const productData = { name: 'Test Product', description: 'Description', price: 99.99, stock: 100, categoryId: '1', ...data, }; const response = await request(app) .post('/api/products') .set('Authorization', `Bearer ${token}`) .send(productData); return response.body.data;} export function authRequest(token: string) { return { get: (url: string) => request(app).get(url).set('Authorization', `Bearer ${token}`), post: (url: string) => request(app).post(url).set('Authorization', `Bearer ${token}`), put: (url: string) => request(app).put(url).set('Authorization', `Bearer ${token}`), delete: (url: string) => request(app).delete(url).set('Authorization', `Bearer ${token}`), };}Testando CRUD Completo
typescript
// src/tests/integration/products.test.tsimport request from 'supertest';import { app } from '../../app';import { createTestUser, authRequest } from '../helpers'; describe('Product Routes', () => { let adminToken: string; let userToken: string; beforeAll(async () => { const admin = await createTestUser({ role: 'ADMIN' }); const user = await createTestUser(); adminToken = admin.token; userToken = user.token; }); describe('GET /api/products', () => { it('deve listar produtos sem autenticação', async () => { const response = await request(app).get('/api/products'); expect(response.status).toBe(200); expect(response.body).toHaveProperty('data'); expect(Array.isArray(response.body.data)).toBe(true); }); it('deve paginar resultados', async () => { const response = await request(app) .get('/api/products') .query({ page: 1, limit: 10 }); expect(response.body).toHaveProperty('pagination'); expect(response.body.pagination.page).toBe(1); }); }); describe('POST /api/products', () => { it('admin pode criar produto', async () => { const response = await authRequest(adminToken) .post('/api/products') .send({ name: 'Novo Produto', price: 199.99, stock: 50, }); expect(response.status).toBe(201); }); it('usuário comum não pode criar', async () => { const response = await authRequest(userToken) .post('/api/products') .send({ name: 'Produto', price: 100 }); expect(response.status).toBe(403); }); }); describe('PUT /api/products/:id', () => { it('deve atualizar produto existente', async () => { // Criar produto primeiro const created = await authRequest(adminToken) .post('/api/products') .send({ name: 'Original', price: 100 }); // Atualizar const response = await authRequest(adminToken) .put(`/api/products/${created.body.data.id}`) .send({ name: 'Atualizado', price: 150 }); expect(response.status).toBe(200); expect(response.body.data.name).toBe('Atualizado'); }); }); describe('DELETE /api/products/:id', () => { it('deve deletar produto', async () => { const created = await authRequest(adminToken) .post('/api/products') .send({ name: 'Para Deletar', price: 50 }); const response = await authRequest(adminToken) .delete(`/api/products/${created.body.data.id}`); expect(response.status).toBe(204); }); });});Resumo
- ✅ Supertest para testar rotas HTTP
- ✅ Banco de teste isolado
- ✅ Helpers para criar dados de teste
- ✅ Testes de autenticação e autorização
- ✅ Testes CRUD completos
Próxima aula: Documentação com Swagger! 📚