Express.js
Testing unitario del backend
Explica cómo probar casos de uso, policies, mappers y utilidades sin levantar Express, usando dependencias falsas y assertions sobre comportamiento.
- Última actualización
- Actualizada
- Nivel
- Fundamentos
Express.js
Explica cómo probar casos de uso, policies, mappers y utilidades sin levantar Express, usando dependencias falsas y assertions sobre comportamiento.
Una prueba unitaria verifica una decisión pequeña con dependencias controladas. En backend, su mejor objetivo suele ser la lógica de negocio, no simular Express completo.
El test unitario ejecuta una unidad en aislamiento razonable:
input
→ caso de uso o función
→ resultado / error / interacción observable“Unidad” no significa obligatoriamente una clase o método. Puede ser una regla, policy, mapper o caso de uso coherente.
Sin tests rápidos, cada cambio exige levantar servidor y dependencias para comprobar reglas. Los tests unitarios:
No prueban integración real con HTTP o PostgreSQL; esa limitación es intencional.
Buenos candidatos:
Mal candidato unitario: comprobar que Express matchea una ruta; una prueba HTTP es más valiosa.
it('rejects an empty order', async () => {
const orders = new InMemoryOrderRepository();
const createOrder = new CreateOrder({ orders });
await expect(createOrder.execute({
customerId: 'cus_1',
items: [],
})).rejects.toBeInstanceOf(EmptyOrderError);
expect(orders.items).toHaveLength(0);
});Implementación funcional simplificada, como repository en memoria. Permite tests legibles, pero puede diferir de PostgreSQL en constraints/concurrencia.
Devuelve respuestas predeterminadas.
Registra llamadas para verificarlas.
Define expectativas estrictas de interacción.
No uses todos los términos como sinónimos. Prefiere verificar resultados; las expectativas internas vuelven tests frágiles.
Factories o constructores permiten sustituir dependencias:
const payments: PaymentGateway = {
createIntent: vi.fn().mockResolvedValue({ id: 'pay_1' }),
};No necesitas un contenedor complejo. Pasar objetos explícitos suele bastar.
Date.now(), UUIDs y random dificultan tests. Inyecta funciones:
const clock = { now: () => new Date('2026-07-24T12:00:00Z') };
const ids = { next: () => 'ord_123' };Así verificas expiración, orden y resultados deterministas.
Prueba no solo que lanza, sino la categoría correcta y que no dejó efectos parciales.
await expect(useCase.execute(input)).rejects.toMatchObject({
code: 'INSUFFICIENT_STOCK',
});
expect(outbox.events).toHaveLength(0);Utiliza matrices:
owner + own order → allow
owner + other tenant → deny
manager + branch order → allow
viewer + cancel → denyEsto revela combinaciones olvidadas mejor que un test aislado.
Un test que exige que se llame exactamente repository.find antes de repository.save puede romper durante un refactor correcto.
Verifica interacción solo cuando forma parte del contrato, como “no llamar al gateway si la validación falla”.
it.each([
['pending', 'confirm', true],
['cancelled', 'confirm', false],
['delivered', 'cancel', false],
])('%s + %s', (state, action, allowed) => {
expect(canTransition(state, action)).toBe(allowed);
});Útil para reglas con combinaciones; evita ocultar demasiado contexto en tablas enormes.
Para invariantes matemáticas o combinaciones extensas, herramientas generan inputs:
No sustituye ejemplos de negocio legibles.
Un fake repository puede permitir duplicados que PostgreSQL rechaza. Mantén contract tests compartidos o integration tests para verificar que fake y real cumplen comportamiento relevante.
CancelOrder:
La unidad prueba decisiones; PostgreSQL y HTTP se prueban después.
Porcentaje alto no prueba calidad. Prioriza:
No escribas tests vacíos solo para cubrir getters o wiring.
Coloca tests cerca del módulo o en estructura paralela. Usa nombres que expresen comportamiento:
rejects cancellation after deliverymejor que:
cancelOrder test 4Cada test debe poder ejecutarse solo y en cualquier orden. Limpia timers, globals y mocks. Evita compartir estado mutable entre tests.
Testing de integración HTTP verifica el pipeline real de Express, middleware y contrato serializado.