Arquitectura · 28 de octubre de 2026 · Daniel Murillo
Cómo probar una API: contrato, E2E y carga
La meta no es tener muchas pruebas, sino evidencia rápida de que la API cumple su contrato bajo condiciones relevantes.
La meta no es tener muchas pruebas; es tener evidencia rápida y confiable de que la API cumple su contrato bajo condiciones relevantes.
Introducción
Una API puede devolver 200 OK y seguir siendo incorrecta: datos incompletos, filtrado roto, contrato incompatible o colapso bajo concurrencia.
Las pruebas tienen objetivos distintos. Combina niveles que detecten fallos diferentes con un costo de mantenimiento razonable.
/\
/ \ E2E / carga (pocas, caras, alto valor de integración)
/----\
/ \ Contrato / integración
/--------\
/ \ Unitarias / lógica (muchas, rápidas)
--------------
No hay una pirámide universal. Ajusta según el costo de fallar en producción.
1. Tipos de pruebas
| Tipo | Qué verifica | Límite típico |
|---|---|---|
| Unitarias | Lógica aislada | No prueban SQL real ni red |
| Integración | Componentes juntos (API + Postgres) | Más lentas; necesitan entorno |
| Contrato | Acuerdo consumidor ↔ proveedor | No prueba toda la lógica de negocio |
| E2E | Flujo completo | Frágiles y costosas si se abusa |
| Carga | Comportamiento bajo tráfico | No sustituye pruebas funcionales |
| Seguridad | AuthZ, validación, límites | Debe ser deliberada, no un afterthought |
2. Ejemplo: Vitest + Supertest
Exporta la app sin abrir el puerto en el mismo módulo del listen.
import request from "supertest";
import { describe, expect, it } from "vitest";
import { app } from "../src/app";
describe("GET /health", () => {
it("returns a healthy response", async () => {
const response = await request(app).get("/health");
expect(response.status).toBe(200);
expect(response.body).toEqual({ status: "ok" });
});
});
Esto valida el contrato HTTP de la ruta en proceso, no que Postgres o el despliegue funcionen.
3. Integración con base de datos
Cuando el comportamiento depende de SQL, prueba contra una base real y aislada (p. ej. Testcontainers o un Postgres de CI). Los mocks no verifican índices, constraints ni transacciones.
Buenas prácticas:
- Migraciones aplicadas en el setup.
- Datos aislados por prueba o limpieza fiable.
- Sin compartir DB entre workers paralelos sin aislamiento.
- Probar errores y restricciones relevantes.
4. Contratos (OpenAPI / Pact)
Un contrato fija rutas, campos, tipos y estados. OpenAPI describe el acuerdo; Pact modela expectativas consumidor-proveedor.
paths:
/users/{id}:
get:
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
"200":
description: Usuario encontrado
content:
application/json:
schema:
type: object
required: [id, email]
properties:
id:
type: string
email:
type: string
format: email
"404":
description: Usuario no encontrado
Mantén la especificación alineada con la implementación (validación en CI). Un contrato no garantiza la lógica de negocio completa.
5. E2E
Flujos críticos: registro → login → crear recurso → consultarlo. Detectan problemas de secuencia y configuración, pero son lentas. No pongas todas las reglas de negocio solo en E2E.
6. Carga con k6
Define una pregunta: «¿este endpoint cumple p95 bajo X ms con Y RPS y tasa de error bajo Z?»
import http from "k6/http";
import { check, sleep } from "k6";
export const options = {
vus: 10,
duration: "30s",
thresholds: {
http_req_failed: ["rate<0.01"],
http_req_duration: ["p(95)<300"],
},
};
export default function () {
const response = http.get(`${__ENV.BASE_URL}/health`);
check(response, { "status is 200": (r) => r.status === 200 });
sleep(1);
}
/health no representa la carga del negocio. Distingue load, stress, spike y soak. Documenta hardware, datos y perfil de peticiones.
7. Qué observar en carga
- Throughput y errores
- Latencia p50 / p95 / p99
- CPU, memoria, event loop
- Pool de DB y waits
- Dependencias externas
- Colas y reintentos
8. Seguridad y caminos negativos
- Token ausente, inválido o expirado
- Usuario sin permiso / IDOR (recurso ajeno)
- Payloads malformados y campos extra
- Rate limits y tamaños
- Idempotencia y reintentos
- Timeouts de dependencias
- Datos sensibles en respuestas y logs
9. Herramientas
| Herramienta | Uso |
|---|---|
| Vitest / Jest | Unitarias e integración ligera |
| Supertest | HTTP contra la app Node |
| Playwright | E2E de navegador |
| Pact | Contrato consumidor-proveedor |
| OpenAPI tooling | Contrato HTTP |
| k6 | Carga |
| Testcontainers | Dependencias reales en CI |
| OWASP ZAP | Seguridad dinámica (con cuidado) |
10. CI/CD
- Lint y tipos
- Unitarias rápidas
- Integración con deps aisladas
- Contratos
- Build
- E2E en entorno controlado
- Carga como gate aparte en cambios críticos
No todo en cada commit: feedback rápido primero; lo caro cuando aporta valor.
Conclusión
Las pruebas no son una competencia por cantidad de casos. Son una estrategia para detectar fallos en el nivel útil al costo adecuado.
Unitarias para lógica, integración para colaboraciones reales, contratos para acuerdos, E2E para recorridos críticos y carga para capacidad declarada.
En esta serie
- Autenticación
- Observabilidad
- SQL, query builders y ORMs
- Cómo probar una API (esta nota)
- Contenedores y Twelve-Factor
Lecturas relacionadas
- Observabilidad
- Benchmarks y decisiones de arquitectura
- Optimizar APIs REST
- Herramienta: qué protocolo de API te conviene