Pruebas de Integración
Pruebas que verifican la API completa con base de datos real.
Instalación
bash
npm install -D supertest @types/supertestConfiguración de Test
typescript
// src/test/setup.tsimport { prisma } from '../config/database'; beforeAll(async () => { // Conectar a la BD de pruebas await prisma.$connect();}); afterAll(async () => { await prisma.$disconnect();}); beforeEach(async () => { // Limpiar tablas antes de cada prueba await prisma.orderItem.deleteMany(); await prisma.order.deleteMany(); await prisma.review.deleteMany(); await prisma.product.deleteMany(); await prisma.category.deleteMany(); await prisma.user.deleteMany();});Jest Config para Integración
javascript
// jest.integration.config.jsmodule.exports = { preset: 'ts-jest', testEnvironment: 'node', roots: ['<rootDir>/src'], testMatch: ['**/*.integration.spec.ts'], setupFilesAfterEnv: ['<rootDir>/src/test/setup.ts'], testTimeout: 30000,};Variables de Entorno
env
# .env.testDATABASE_URL="postgresql://postgres:postgres@localhost:5432/ecommerce_test"JWT_SECRET="test-secret"Helpers de Prueba
typescript
// src/test/helpers.tsimport { prisma } from '../config/database';import bcrypt from 'bcryptjs';import jwt from 'jsonwebtoken'; export const createTestUser = async (data: Partial<User> = {}) => { return prisma.user.create({ data: { name: 'Test User', email: `test-${Date.now()}@email.com`, password: await bcrypt.hash('123456', 10), role: 'USER', ...data, }, });}; export const createTestCategory = async () => { return prisma.category.create({ data: { name: `Category ${Date.now()}`, slug: `category-${Date.now()}`, }, });}; export const getAuthToken = (userId: string) => { return jwt.sign({ sub: userId }, process.env.JWT_SECRET!, { expiresIn: '1h', });};Prueba de Integración
typescript
// src/routes/__tests__/products.integration.spec.tsimport request from 'supertest';import { app } from '../../app';import { createTestUser, createTestCategory, getAuthToken } from '../../test/helpers';import { prisma } from '../../config/database'; describe('Products API', () => { describe('GET /api/products', () => { it('debe retornar lista de productos', async () => { const category = await createTestCategory(); await prisma.product.createMany({ data: [ { name: 'Product 1', slug: 'product-1', price: 100, categoryId: category.id }, { name: 'Product 2', slug: 'product-2', price: 200, categoryId: category.id }, ], }); const response = await request(app) .get('/api/products') .expect(200); expect(response.body.data).toHaveLength(2); expect(response.body.pagination).toBeDefined(); }); it('debe filtrar por búsqueda', async () => { const category = await createTestCategory(); await prisma.product.createMany({ data: [ { name: 'iPhone 15', slug: 'iphone-15', price: 999, categoryId: category.id }, { name: 'MacBook Pro', slug: 'macbook-pro', price: 1999, categoryId: category.id }, ], }); const response = await request(app) .get('/api/products?search=iphone') .expect(200); expect(response.body.data).toHaveLength(1); expect(response.body.data[0].name).toBe('iPhone 15'); }); }); describe('POST /api/products', () => { it('debe crear producto (admin)', async () => { const admin = await createTestUser({ role: 'ADMIN' }); const category = await createTestCategory(); const token = getAuthToken(admin.id); const response = await request(app) .post('/api/products') .set('Authorization', `Bearer ${token}`) .send({ name: 'Nuevo Producto', price: 299.99, categoryId: category.id, }) .expect(201); expect(response.body.name).toBe('Nuevo Producto'); expect(response.body.slug).toBe('nuevo-producto'); }); it('debe rechazar sin autenticación', async () => { await request(app) .post('/api/products') .send({ name: 'Test', price: 100 }) .expect(401); }); it('debe rechazar usuarios no admin', async () => { const user = await createTestUser({ role: 'USER' }); const token = getAuthToken(user.id); await request(app) .post('/api/products') .set('Authorization', `Bearer ${token}`) .send({ name: 'Test', price: 100 }) .expect(403); }); });});Scripts
json
{ "scripts": { "test:integration": "dotenv -e .env.test -- jest --config jest.integration.config.js --runInBand" }}Resumen
- ✅ Supertest para HTTP requests
- ✅ Base de datos de prueba aislada
- ✅ Helpers reutilizables
- ✅ Pruebas de autenticación/autorización
Próxima clase: Documentación con Swagger! 🚀