Pular para o conteúdoPedro Farbo
Lição 18 / 2545 min

Testes de Integração

Testes de Integração

Testes de integração verificam se os componentes funcionam juntos corretamente.

Setup com Supertest

bash
npm install -D supertest @types/supertest

Banco 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! 📚

Gostou do conteúdo? Sua contribuição ajuda a manter tudo online e gratuito!

PIX:0737160d-e98f-4a65-8392-5dba70e7ff3e