Pruebas (Testing)
🎯 Objetivo
Regla
15.1: Se debe implementar una estrategia de pruebas exhaustiva y automatizada para todas las APIs REST, cubriendo diferentes niveles (unitarias, integración, contrato, E2E). 15.2: El objetivo es asegurar la correctitud, fiabilidad, rendimiento y seguridad de la API, y facilitar la refactorización y evolución continua.
📖 Concepto y Definición
Regla
15.3: Las pruebas de API son obligatorias y deben incluir como mínimo los siguientes tipos:
- Pruebas Unitarias: Deben verificar la lógica de negocio aislada dentro de los componentes individuales del servicio (ej. controladores, servicios, modelos) sin depender de componentes externos como bases de datos o servicios de terceros (usando mocks/stubs).
- Pruebas de Integración: Deben verificar la interacción entre los componentes internos de la API y con servicios externos como bases de datos. No deben probar la interacción con otras APIs externas (eso sería E2E).
- Pruebas de Contrato (API): Deben verificar que la API cumple con su contrato definido (ej. especificación OpenAPI). Esto incluye validar URIs, métodos, encabezados, códigos de estado, y la estructura y tipos de datos de las solicitudes/respuestas.
- Pruebas End-to-End (E2E) (Recomendado): Pueden verificar flujos completos a través de la API, posiblemente involucrando otras APIs o sistemas, desde la perspectiva del cliente.
- Pruebas de Seguridad (Recomendado): Deben evaluar la API contra vulnerabilidades comunes (ver Sección 7).
- Pruebas de Rendimiento (Recomendado): Deben medir la latencia, el throughput y la escalabilidad de la API bajo carga.
🤔 ¿Por qué es Importante? / Beneficios Clave (Contexto)
- Calidad y Fiabilidad: Asegura que la API funcione como se espera.
- Detección Temprana de Errores: Reduce el costo de arreglar bugs.
- Confianza en Cambios: Facilita la refactorización y adición de nuevas funcionalidades.
- Documentación Viva: Las pruebas (especialmente las de contrato) sirven como ejemplos de uso.
- Regresión: Evita que funcionalidades previamente correctas se rompan.
✅ Buenas Prácticas y Recomendaciones Clave
Regla
15.4: Todas las pruebas deben ser automatizadas y ejecutadas como parte del pipeline de Integración Continua (CI) antes de cualquier despliegue.
15.5: Se debe aspirar a una alta cobertura de código con pruebas unitarias y de integración.
15.6: Las pruebas de contrato son obligatorias y deben validar la conformidad con la especificación OpenAPI (Norma 9.4). Se pueden usar herramientas que validen las interacciones HTTP contra el schema OAS.
15.7: Las pruebas deben cubrir casos de éxito, casos de error (ej. diferentes códigos 4xx, 5xx) y casos límite.
15.8: Los datos de prueba deben ser realistas y gestionados de forma que las pruebas sean repetibles y no interfieran entre sí.
15.9: Para pruebas de integración que involucren bases de datos, se debe usar una base de datos de prueba dedicada y asegurar que esté en un estado conocido antes de cada ejecución de prueba (ej. rollback de transacciones, limpieza de datos).
15.10: Se deben mockear o stubbear las dependencias externas (otras APIs, servicios de terceros) en pruebas unitarias y de integración para asegurar aislamiento y determinismo.
15.11: Las pruebas de seguridad y rendimiento, aunque listadas como recomendadas en 15.3, son altamente recomendadas y deben considerarse obligatorias para APIs críticas o expuestas públicamente.
Don'ts (Prohibido):
- 15.12: No se debe desplegar código de API a producción sin que pasen todas las pruebas automatizadas relevantes.
- 15.13: No se deben escribir pruebas frágiles que fallen con cambios menores no relacionados.
- 15.14: No se deben depender de datos volátiles o entornos externos inestables para pruebas unitarias o de integración.
- 15.15: No se deben ignorar los fallos de prueba.
💡 Ejemplos Prácticos (Ilustrativos)
Ejemplo 1: Prueba de Contrato (Pseudo-código con herramienta tipo Dredd o similar)
Asumiendo una especificación OpenAPI openapi.yaml:
// (Conceptual - la sintaxis depende de la herramienta)
// Hook para preparar datos antes de una prueba específica
hooks.before('/users > POST > 201', (transaction) => {
transaction.request.body = JSON.stringify({ username: 'newuser', email: 'new@example.com' });
});
// Prueba: POST /users crea un usuario y devuelve 201
// La herramienta validará que la solicitud y respuesta coincidan con openapi.yaml
// para el endpoint POST /users y la respuesta 201.
Ejemplo 2: Prueba de Integración (Ejemplo con un framework de testing de Node.js como Jest + Supertest)
const request = require('supertest');
const app = require('../app'); // Tu aplicación API
const db = require('../db'); // Módulo de base deatos
describe('GET /v1/users/:userId', () => {
beforeAll(async () => {
// Preparar base de datos de prueba
await db.query("INSERT INTO users (id, username) VALUES ('test-user-1', 'tester')");
});
afterAll(async () => {
// Limpiar base de datos de prueba
await db.query("DELETE FROM users WHERE id = 'test-user-1'");
await db.close();
});
it('debe devolver 200 y los datos del usuario si existe', async () => {
const response = await request(app).get('/v1/users/test-user-1');
expect(response.statusCode).toBe(200);
expect(response.body.data.userId).toBe('test-user-1');
expect(response.body.data.username).toBe('tester');
});
it('debe devolver 404 si el usuario no existe', async () => {
const response = await request(app).get('/v1/users/non-existent-user');
expect(response.statusCode).toBe(404);
expect(response.body.error.code).toBe('RESOURCE_NOT_FOUND'); // Validar formato de error (Norma 11.4)
});
});
🛠️ Herramientas y Consideraciones Adicionales (Contexto)
- Frameworks de Pruebas Unitarias: Jest, Mocha, PyTest, JUnit, NUnit, etc. (dependiendo del lenguaje).
- Bibliotecas de Aserción HTTP: Supertest (Node.js), REST Assured (Java), cliente HTTP del framework (Python: requests + pytest).
- Herramientas de Pruebas de Contrato: Dredd, PactumJS, Prism (de Stoplight).
- Mocks/Stubs: Sinon.JS (Node.js), Mockito (Java), unittest.mock (Python).
- Pruebas de Carga: k6, JMeter, Locust, Artillery.
- Escáneres de Seguridad: OWASP ZAP, Burp Suite.
- Cobertura de Código: Istanbul (JS), Cobertura/JaCoCo (Java), coverage.py (Python).
- Datos de Prueba: Se pueden usar bibliotecas de generación de datos falsos (ej. Faker.js) o estrategias de plantillas de datos.