Saltearse al contenido

Guía de Pruebas

Aprende a probar tus aplicaciones Veloce-TS de manera efectiva.

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.

Ventana de terminal
# Las pruebas se ejecutan con Bun
bun test
# Modo watch
bun test --watch
# Cobertura
bun test --coverage

Crea una instancia de prueba de tu aplicación.

import { createTestApp } from 'veloce-ts/testing';
const app = createTestApp();

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 fluente
const response = await client.get('/users');
await response.expectOk().expectArrayLength(2);

Helpers de autenticación:

// Cliente con token pre-establecido
const authClient = client.withToken('mi-jwt-token');
// Login automático
const loggedIn = await client.loginAs({
email: 'admin@ejemplo.com',
password: 'contraseña123'
});
// Registrar y hacer login en una sola llamada
const nuevoUsuario = await client.registerAndLogin({
nombre: 'Nuevo Usuario',
email: 'nuevo@ejemplo.com',
password: 'contraseña123'
});

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 respuesta

Simula una dependencia en el contenedor DI.

import { mockDependency } from 'veloce-ts/testing';
const mockDb = {
getUsers: async () => [{ id: 1, name: 'Prueba' }],
};
mockDependency(DatabaseService, mockDb);
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);
});
});
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');
});
});
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);
});
});
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');
});
});

El nuevo TestClient incluye helpers integrados para probar endpoints autenticados.

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();
});
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');
});
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');
});
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();
});
// ✓ Bueno
it('debería retornar 404 cuando el usuario no existe', async () => {});
// ✗ Malo
it('prueba usuario', async () => {});
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);
});
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
});
});
describe('UserController', () => {
let db: DatabaseService;
beforeEach(async () => {
db = new InMemoryDatabaseService();
await db.connect();
});
afterEach(async () => {
await db.clear();
await db.disconnect();
});
});
Ventana de terminal
bun test
Ventana de terminal
bun test user.test.ts
Ventana de terminal
bun test --watch
Ventana de terminal
bun test --coverage