Pruebas de Integridad de Datos con DTOs y Joy en APIs REST

Clase 9 de 25Curso de End to End Testing para APIs REST con Node.js

Contenido del curso

Pruebas a la API de Fake Store

Pruebas en Entornos de Desarrollo Avanzados

Resumen

Validar la integridad de los datos que recibe una API es tan importante como construir los endpoints mismos. Si un usuario envía un email mal formado o un password demasiado corto, el backend debe responder con un status code 400 (bad request) y nunca permitir que esa información llegue a la base de datos. Aquí se explica cómo escribir pruebas end-to-end que verifiquen exactamente ese comportamiento, utilizando Supertest y los DTO (Data Transfer Objects) validados con la librería Joy.

¿Qué son los DTO y por qué validar su integridad con pruebas?

Los DTO, o Data Transfer Objects, son esquemas que definen la estructura y las reglas de los datos que un endpoint acepta. En este caso, el endpoint de creación de usuarios requiere un email válido y un password con al menos ocho caracteres. La librería Joy se encarga de verificar esas reglas dentro de un middleware llamado Validator Handler [01:50].

El flujo funciona así: cuando llega un request, el Validator Handler evalúa los datos contra el esquema. Si hay un error, utiliza Boom para generar una respuesta de bad request (400) y el proceso se detiene antes de llegar a la capa de servicios. Si los datos son correctos, el request continúa su camino normal.

¿Cómo se organiza un archivo de pruebas por endpoint?

Una convención muy común en proyectos Node es crear un archivo de pruebas por cada dominio de endpoint [04:15]. Por ejemplo, todo lo relacionado con el path /users (crear, actualizar, obtener, eliminar) se agrupa en un solo archivo llamado users.e2e.js. Dentro de ese archivo se anidan describes para cada operación:

  • Un describe para las pruebas de GET /users.
  • Un describe para las pruebas de POST /users.
  • Un describe para las pruebas de PATCH /users.

Esta estructura permite tener múltiples pruebas por endpoint sin perder organización.

¿Cómo aplicar el mantra de las tres A en pruebas end-to-end?

Cada prueba sigue el patrón Arrange, Act, Assert [06:22]:

  • Arrange: preparar los datos de entrada. Por ejemplo, un objeto con un email correcto pero un password inválido.
  • Act: ejecutar el request con Supertest usando api.post('/api/v1/users').send(inputData).
  • Assert: verificar que la respuesta cumpla la hipótesis esperada.

Un detalle crítico al usar Supertest: siempre incluir el slash inicial en el path [07:55]. Omitirlo provoca que el endpoint no sea encontrado y puede consumir horas de debugging innecesario.

javascript test('should return 400 bad request with invalid password', async () => { // Arrange const inputData = { email: 'nicolas@mail.com', password: '123', }; // Act const { statusCode, body } = await api .post('/api/v1/users') .send(inputData); // Assert expect(statusCode).toBe(400); expect(body.message).toMatch(/password/); });

¿Cómo probar múltiples casos de validación en un mismo endpoint?

No basta con una sola prueba. Se deben cubrir distintos escenarios de datos inválidos [11:38]. Por ejemplo:

  • Enviar un password que no cumple la longitud mínima con un email correcto.
  • Enviar un email con formato incorrecto y un password válido.

En cada caso se valida que el status code sea 400 y que el mensaje de error contenga la palabra clave correspondiente, ya sea password o email. Esto se logra con toMatch y una expresión regular:

javascript expect(body.message).toMatch(/email/);

¿Por qué usar destructuring en las respuestas de las pruebas?

El objeto response de Supertest contiene mucha información. Utilizando destructuring de JavaScript, se pueden extraer solo las propiedades necesarias como statusCode y body [13:10]. Esto mejora la legibilidad del código sin cambiar el comportamiento de la prueba:

javascript const { statusCode, body } = await api .post('/api/v1/users') .send(inputData);

Ambas formas son válidas. La elección entre usar el objeto completo o destructuring depende de cuántas propiedades se necesiten consultar.

¿Cómo manejar los logs que ensucian la terminal durante las pruebas?

Al correr la aplicación productiva completa, los middlewares de manejo de errores pueden imprimir información en consola que dificulta leer los resultados de las pruebas [10:50]. Esos logs son útiles en producción para herramientas como Sentry o Datadog, pero durante las pruebas conviene desactivarlos. Una solución es detectar automáticamente si el entorno es de pruebas y omitir la impresión, o simplemente comentar temporalmente el console.log del middleware de errores.

También es recomendable usar la opción --forceExit en Jest y, si aparecen falsas alarmas sobre open handles, verificar que todos los requests asíncronos estén correctamente esperados con await [10:02].

Estas pruebas garantizan que la capa de validación de datos funcione correctamente a lo largo del tiempo, protegiendo la base de datos de información incorrecta. ¿Has enfrentado situaciones donde un slash faltante te costó horas de debugging? Comparte tu experiencia.