Pruebas en JavaScript: Tests Unit, Integration y E2E
Las pruebas son una parte esencial del desarrollo de cualquier software de calidad. En el mundo de JavaScript (especialmente en el ecosistema Node.js), existen tres niveles principales de pruebas: unit tests (pruebas unitarias), integration tests (pruebas de integración) y E2E tests (pruebas de extremo a extremo). Cada uno resuelve sus propias tareas y se utiliza en diferentes etapas del desarrollo. En este artículo, analizaremos cada tipo, mostraremos ejemplos de código y daremos recomendaciones prácticas.
1. Unit tests (pruebas unitarias)
Los unit tests verifican unidades de código individuales y aisladas: funciones, métodos o clases. El objetivo principal es asegurarse de que cada "unidad" funcione correctamente independientemente del resto del sistema. En Node.js, para esto se utilizan con mayor frecuencia los frameworks Jest o Mocha con Chai.
Ejemplo de unit test con Jest
Supongamos que tenemos una función simple para calcular un descuento:
// discount.jsfunction calculateDiscount(price, percent) { if (typeof price !== 'number' || typeof percent !== 'number') { throw new Error('Ambos argumentos deben ser números'); } if (price < 0 || percent < 0 || percent > 100) { throw new Error('Valores incorrectos'); } return price - (price * percent / 100);}
module.exports = calculateDiscount;Test para esta función:
// discount.test.jsconst calculateDiscount = require('./discount');
describe('calculateDiscount', () => { test('calcula correctamente un 10% de descuento de 1000', () => { expect(calculateDiscount(1000, 10)).toBe(900); });
test('devuelve 0 con un 100% de descuento', () => { expect(calculateDiscount(500, 100)).toBe(0); });
test('lanza un error con precio negativo', () => { expect(() => calculateDiscount(-100, 10)).toThrow('Valores incorrectos'); });
test('lanza un error con argumentos no numéricos', () => { expect(() => calculateDiscount('100', 10)).toThrow('Ambos argumentos deben ser números'); });});- Ventajas: Rápidos, fáciles de escribir, señalan exactamente el módulo problemático.
- Desventajas: No verifican la interacción entre componentes.
- Cuándo usarlos: Para funciones de lógica de negocio, utilidades, validadores.
2. Integration tests (pruebas de integración)
Los integration tests verifican cómo diferentes módulos o servicios trabajan juntos. Por ejemplo, la interacción con una base de datos, una API externa o el sistema de archivos. En Node.js, para estas pruebas se suele usar Supertest (para peticiones HTTP) y Jest.
Ejemplo de integration test para una aplicación Express
Supongamos que tenemos un servidor Express simple con un endpoint para crear un usuario:
// server.jsconst express = require('express');const app = express();app.use(express.json());
const users = [];
app.post('/users', (req, res) => { const { name, email } = req.body; if (!name || !email) { return res.status(400).json({ error: 'Name and email are required' }); } const user = { id: users.length + 1, name, email }; users.push(user); res.status(201).json(user);});
module.exports = app;Integration test con Supertest:
// server.test.jsconst request = require('supertest');const app = require('./server');
describe('POST /users', () => { test('crea un nuevo usuario con datos válidos', async () => { const response = await request(app) .post('/users') .send({ name: 'Iván', email: 'ivan@example.com' });
expect(response.statusCode).toBe(201); expect(response.body).toHaveProperty('id'); expect(response.bo