Express.js
Testing de integración HTTP
Explica cómo probar routing, middleware, validación, auth, status, headers y persistencia mediante una aplicación Express.js real y dependencias controladas.
- Última actualización
- Actualizada
- Nivel
- Aplicación
Express.js
Explica cómo probar routing, middleware, validación, auth, status, headers y persistencia mediante una aplicación Express.js real y dependencias controladas.
Una prueba de integración HTTP verifica que rutas, middleware, validación, errores y serialización colaboran como un contrato real de Express.
La unidad bajo prueba es la aplicación HTTP construida con dependencias controladas:
HTTP request simulada
↓
Express real
↓
middleware + router + handler
↓
dependencias fake o test DB
↓
HTTP responseUn handler puede funcionar solo y aun así fallar porque:
La prueba HTTP captura ese wiring.
export function createApp(deps: AppDependencies) {
const app = express();
app.use(express.json({ limit: '100kb' }));
app.use('/orders', createOrdersRouter(deps));
app.use(notFoundHandler);
app.use(errorHandler(deps.logger));
return app;
}No abre puerto y permite sustituir dependencias.
const app = createApp({
createOrder: fakeCreateOrder,
authenticate: fakeAuth,
logger: silentLogger,
});
await request(app)
.post('/orders')
.send(validBody)
.expect(201)
.expect('Content-Type', /json/)
.expect(({ body, headers }) => {
expect(body.data.id).toBe('ord_123');
expect(headers.location).toBe('/orders/ord_123');
});Supertest puede invocar la app sin gestionar un puerto manualmente.
Para POST /orders:
No todas las APIs usan exactamente 422; prueba el contrato decidido.
Aísla el boundary HTTP y hace tests rápidos.
Verifica query, constraints y transacciones, pero aumenta setup.
Una estrategia útil combina ambos: muchas pruebas HTTP con casos de uso controlados y algunas rutas críticas contra DB real.
No desactives toda auth silenciosamente. Puedes:
Debe quedar claro qué capa está siendo ejercitada.
Opciones:
Evita compartir datos entre tests o depender del orden.
Construye datos mínimos mediante builders:
const orderInput = buildCreateOrderInput({
items: [{ productId: product.id, quantity: 2 }],
});Los builders expresan intención mejor que fixtures gigantes globales.
await request(app)
.post('/orders')
.send({ items: [] })
.expect(422)
.expect(({ body }) => {
expect(body).toMatchObject({
code: 'VALIDATION_ERROR',
status: 422,
});
expect(body.requestId).toEqual(expect.any(String));
});No asserts mensajes humanos completos salvo que sean parte estable.
Pruebas normales no siempre simulan abort/backpressure. Para esas rutas puede necesitarse un servidor real, streams controlados o tests específicos de Node.
Valida responses contra schema OpenAPI. Esto detecta campos faltantes, tipos y status no documentados.
No reemplaza assertions de negocio: una response puede cumplir schema y ser semánticamente incorrecta.
Pueden servir para contratos pequeños, pero snapshots grandes aceptados sin revisión ocultan cambios. Prefiere assertions explícitas en campos críticos.
Para idempotencia o stock, envía requests simultáneas contra DB real y comprueba invariantes, no solo status individuales.
Prueba crear pedido multi-tenant:
Esto cruza HTTP, auth, scope y persistencia.
Una suite de integración puede ser paralela con databases aisladas, pero paralelizar sobre tablas compartidas produce flakiness. Optimiza después de hacerla determinista.
End-to-end testing amplía la verificación hacia infraestructura y dependencias desplegadas.