Guía de Pruebas
Guía de Pruebas
Sección titulada «Guía de Pruebas»Aprende a probar tus aplicaciones Veloce-TS de manera efectiva.
Tabla de Contenidos
Sección titulada «Tabla de Contenidos»- Introducción
- Utilidades de Prueba
- Pruebas Unitarias
- Pruebas de Integración
- Pruebas con Dependencias
- Probando Autenticación
- Mejores Prácticas
Introducción
Sección titulada «Introducción»Veloce proporciona utilidades de prueba para facilitar el testing de tus aplicaciones. El framework usa el test runner integrado de Bun, que es rápido y tiene una API compatible con Jest.
Configuración
Sección titulada «Configuración»# Las pruebas se ejecutan con Bunbun test
# Modo watchbun test --watch
# Coberturabun test --coverageUtilidades de Prueba
Sección titulada «Utilidades de Prueba»createTestApp()
Sección titulada «createTestApp()»Crea una instancia de prueba de tu aplicación.
import { createTestApp } from 'veloce-ts/testing';
const app = createTestApp();TestClient
Sección titulada «TestClient»Cliente HTTP para hacer peticiones de prueba con una API de aserciones fluente.
import { TestClient } from 'veloce-ts/testing';
const client = new TestClient(app);
// Petición básica con aserción fluenteconst response = await client.get('/users');await response.expectOk().expectArrayLength(2);Helpers de autenticación:
// Cliente con token pre-establecidoconst authClient = client.withToken('mi-jwt-token');
// Login automáticoconst loggedIn = await client.loginAs({ email: 'admin@ejemplo.com', password: 'contraseña123'});
// Registrar y hacer login en una sola llamadaconst nuevoUsuario = await client.registerAndLogin({ nombre: 'Nuevo Usuario', email: 'nuevo@ejemplo.com', password: 'contraseña123'});TestResponse
Sección titulada «TestResponse»Envoltorio de respuesta devuelto por todos los métodos de TestClient.
Propiedades: status, ok, headers, body, text
Métodos de aserción:
response .expectStatus(201) // código de estado específico .expectOk() // 200 .expectCreated() // 201 .expectNotFound() // 404 .expectUnauthorized() // 401 .expectForbidden() // 403 .expectBadRequest() // 400 .expectJson({ id: 1 }) // coincidencia parcial del body .expectField('email') // campo existe .expectField('rol', 'admin') // valor del campo .expectHeader('X-Request-ID') // header existe .expectArrayLength(3) // longitud del array en respuestamockDependency()
Sección titulada «mockDependency()»Simula una dependencia en el contenedor DI.
import { mockDependency } from 'veloce-ts/testing';
const mockDb = { getUsers: async () => [{ id: 1, name: 'Prueba' }],};
mockDependency(DatabaseService, mockDb);Pruebas Unitarias
Sección titulada «Pruebas Unitarias»Probando Controladores
Sección titulada «Probando Controladores»import { describe, it, expect, beforeEach } from 'bun:test';import { createTestApp, TestClient } from 'veloce-ts/testing';import { UserController } from './controllers/user.controller';
describe('UserController', () => { let app: Veloce; let client: TestClient;
beforeEach(() => { app = createTestApp(); app.include(UserController); client = new TestClient(app); });
it('debería obtener todos los usuarios', async () => { const response = await client.get('/users'); await response.expectOk().expectArrayLength(2); });
it('debería crear usuario', async () => { const userData = { name: 'John Doe', email: 'john@example.com', };
const response = await client.post('/users', userData); await response.expectCreated().expectJson(userData);
const data = response.body; expect(data.name).toBe(userData.name); });
it('debería validar datos de usuario', async () => { const invalidData = { name: 'J', // Muy corto email: 'email-invalido', };
const response = await client.post('/users', invalidData);
expect(response.status).toBe(422); });});Probando Servicios
Sección titulada «Probando Servicios»import { describe, it, expect } from 'bun:test';import { UserService } from './services/user.service';
describe('UserService', () => { it('debería obtener usuarios', async () => { const service = new UserService(); const users = await service.getUsers();
expect(users).toBeArray(); });
it('debería encontrar usuario por id', async () => { const service = new UserService(); const user = await service.getUserById('1');
expect(user).toBeDefined(); expect(user?.id).toBe('1'); });});Pruebas de Integración
Sección titulada «Pruebas de Integración»Probando Aplicación Completa
Sección titulada «Probando Aplicación Completa»import { describe, it, expect, beforeAll, afterAll } from 'bun:test';import { Veloce } from 'veloce-ts';import { UserController } from './controllers/user.controller';import { DatabaseService } from './services/database.service';
describe('Pruebas de Integración', () => { let app: Veloce; let server: any;
beforeAll(async () => { app = new Veloce();
// Configurar base de datos const db = new DatabaseService(); await db.connect();
app.getContainer().register(DatabaseService, { scope: 'singleton', factory: () => db, });
app.include(UserController);
server = app.listen(3001); });
afterAll(async () => { server.close(); });
it('debería crear y recuperar usuario', async () => { // Crear usuario const createResponse = await fetch('http://localhost:3001/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'Usuario de Prueba', email: 'test@example.com', }), });
expect(createResponse.status).toBe(201); const createdUser = await createResponse.json();
// Recuperar usuario const getResponse = await fetch(`http://localhost:3001/users/${createdUser.id}`); expect(getResponse.status).toBe(200);
const retrievedUser = await getResponse.json(); expect(retrievedUser.id).toBe(createdUser.id); });});Pruebas con Dependencias
Sección titulada «Pruebas con Dependencias»Simulando Dependencias
Sección titulada «Simulando Dependencias»import { describe, it, expect, beforeEach } from 'bun:test';import { createTestApp, mockDependency, TestClient } from 'veloce-ts/testing';
describe('UserController con Mocks', () => { let app: Veloce; let client: TestClient; let mockDb: any;
beforeEach(() => { app = createTestApp();
// Crear mock mockDb = { getUsers: async () => [ { id: 1, name: 'Usuario Mock 1' }, { id: 2, name: 'Usuario Mock 2' }, ], };
// Reemplazar servicio real con mock mockDependency(DatabaseService, mockDb);
app.include(UserController); client = new TestClient(app); });
it('debería usar base de datos simulada', async () => { const response = await client.get('/users'); const data = await response.json();
expect(data).toHaveLength(2); expect(data[0].name).toBe('Usuario Mock 1'); });});Probando Autenticación
Sección titulada «Probando Autenticación»El nuevo TestClient incluye helpers integrados para probar endpoints autenticados.
Usando withToken
Sección titulada «Usando withToken»it('debería acceder a ruta protegida con token', async () => { const authClient = client.withToken('jwt-token-válido'); const response = await authClient.get('/perfil'); await response.expectOk();});Usando loginAs
Sección titulada «Usando loginAs»it('debería hacer login y acceder a rutas protegidas', async () => { const authClient = await client.loginAs({ email: 'admin@ejemplo.com', password: 'admin123', });
const response = await authClient.get('/admin/dashboard'); await response.expectOk().expectField('rol', 'admin');});Usando registerAndLogin
Sección titulada «Usando registerAndLogin»it('debería registrar un nuevo usuario y acceder al perfil', async () => { const authClient = await client.registerAndLogin({ nombre: 'Nuevo Usuario', email: 'nuevo@ejemplo.com', password: 'contraseña123', });
const response = await authClient.get('/perfil'); await response.expectOk().expectField('email', 'nuevo@ejemplo.com');});Probando Acceso No Autorizado
Sección titulada «Probando Acceso No Autorizado»it('debería rechazar peticiones sin autenticación', async () => { const response = await client.get('/perfil'); await response.expectUnauthorized();});
it('debería rechazar permisos insuficientes', async () => { const userClient = await client.loginAs({ email: 'usuario@ejemplo.com', password: 'pass' }); const response = await userClient.get('/admin/usuarios'); await response.expectForbidden();});Mejores Prácticas
Sección titulada «Mejores Prácticas»1. Usa Nombres de Prueba Descriptivos
Sección titulada «1. Usa Nombres de Prueba Descriptivos»// ✓ Buenoit('debería retornar 404 cuando el usuario no existe', async () => {});
// ✗ Maloit('prueba usuario', async () => {});2. Patrón Arrange-Act-Assert
Sección titulada «2. Patrón Arrange-Act-Assert»it('debería crear usuario', async () => { // Arrange (Preparar) const userData = { name: 'John Doe', email: 'john@example.com', };
// Act (Actuar) const response = await client.post('/users', userData); const data = await response.json();
// Assert (Afirmar) expect(response.status).toBe(201); expect(data.name).toBe(userData.name);});3. Prueba Casos Límite
Sección titulada «3. Prueba Casos Límite»describe('UserController', () => { it('debería manejar lista vacía', async () => { // Probar cuando no existen usuarios });
it('debería manejar formato de ID inválido', async () => { // Probar con ID no numérico });
it('debería manejar campos requeridos faltantes', async () => { // Probar validación });});4. Limpia Después de las Pruebas
Sección titulada «4. Limpia Después de las Pruebas»describe('UserController', () => { let db: DatabaseService;
beforeEach(async () => { db = new InMemoryDatabaseService(); await db.connect(); });
afterEach(async () => { await db.clear(); await db.disconnect(); });});Ejecutando Pruebas
Sección titulada «Ejecutando Pruebas»Ejecutar Todas las Pruebas
Sección titulada «Ejecutar Todas las Pruebas»bun testEjecutar Archivo de Prueba Específico
Sección titulada «Ejecutar Archivo de Prueba Específico»bun test user.test.tsModo Watch
Sección titulada «Modo Watch»bun test --watchCobertura
Sección titulada «Cobertura»bun test --coveragePróximos Pasos
Sección titulada «Próximos Pasos»- Revisa la Guía de Inicio
- Aprende sobre Inyección de Dependencias
- Lee la Referencia API