Skip to content
StackPractices
intermediate Por Mathias Paulenko

Plantilla de Estrategia de Testing de API

Una plantilla para planificar tests de contrato, integración y carga para APIs.

Temas: testing

Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.

Visión General

Las APIs evolucionan continuamente. Un cambio en un endpoint puede romper consumidores, degradar rendimiento o introducir agujeros de seguridad. Una estrategia de testing por capas detecta estos problemas en diferentes etapas: los tests de contrato previenen cambios rotos, los tests de integración verifican comportamiento, y los tests de carga validan rendimiento bajo presión.

Cuándo Usar

Usa este recurso cuando:

  • Diseñas cobertura de testing para una nueva API o microservicio
  • Auditas por qué bugs llegan a producción a pesar de tener unit tests
  • Onboarding de un pipeline CI/CD que incluye gates de validación de API

Solución

# Estrategia de Testing de API: `<Nombre de la API>`

## 1. Pirámide de Testing para APIs

| Capa | Alcance | Ejemplos de Herramientas | Frecuencia | Responsable |
|------|---------|--------------------------|------------|-------------|
| Contrato | Schema de request/response | Pact, Spring Cloud Contract | Cada commit | Productor de API |
| Unitario | Funciones individuales del handler | Jest, pytest, JUnit | Cada commit | Desarrollador |
| Integración | API + base de datos + dependencias | Testcontainers, supertest | Cada PR | Desarrollador |
| E2E | Journey completo del usuario | Postman, Cypress | Nightly | QA |
| Carga | Rendimiento bajo tráfico | k6, Gatling, Locust | Semanal / release | SRE |
| Caos | Resiliencia ante fallas | Gremlin, Chaos Monkey | Mensual | Plataforma |

## 2. Tests de Contrato

### 2.1. Contratos Consumer-Driven

- [ ] Los tests del consumidor publican expectativas a un broker (Pact Broker)
- [ ] El proveedor verifica todos los contratos de consumidores en CI antes de mergear
- [ ] Los cambios rotos disparan un bump de versión mayor y notificación a consumidores
- [ ] La matriz de contratos está documentada: qué consumidor usa qué versión del proveedor

### 2.2. Validación de OpenAPI

- [ ] La especificación OpenAPI es la única fuente de verdad para schemas de request/response
- [ ] CI genera tests desde OpenAPI y valida contra la implementación
- [ ] Los cambios de schema requieren actualizar la spec primero, luego el código

## 3. Tests de Integración

### 3.1. Alcance del Test

- [ ] Happy path: request válido retorna respuesta esperada
- [ ] Paths de error: input inválido retorna 4xx correcto con mensaje útil
- [ ] Paths de auth: token ausente/inválido retorna 401, scope insuficiente retorna 403
- [ ] Dependencia de estado: testear con base de datos real (Testcontainers) o equivalente en memoria
- [ ] Idempotencia: repetir el mismo request produce el mismo resultado

### 3.2. Datos de Test

| Estrategia | Usar Cuando | Notas |
|------------|------------|-------|
| Factory pattern | La mayoría de casos | Generar entidades programáticamente, limpiar después del test |
| Snapshot fixtures | Datos de referencia estables | Versionar respuestas JSON esperadas |
| Base de staging compartida | Integración cross-team | Riesgo de tests flaky por mutación de datos |

## 4. Tests de Carga

### 4.1. Escenarios

| Escenario | Objetivo | Métrica |
|-----------|----------|---------|
| Línea base | Tráfico constante por 10 minutos | p95 latencia < presupuesto |
| Pico | 10x tráfico en 30 segundos | Sin errores, autoscaling dispara |
| Remojo | Tráfico sostenido por 2 horas | Memoria estable, sin leaks de conexiones |
| Estrés | Incrementar tráfico hasta falla | Identificar punto de ruptura de forma segura |

### 4.2. Checklist de Test de Carga

- [ ] El ambiente replica producción (mismos tamaños de instancia, specs de base de datos)
- [ ] El volumen de datos coincide con producción (o escalado proporcionalmente)
- [ ] Los scripts de test están parametrizados para evitar cache hits en requests idénticas
- [ ] Los dashboards de monitoreo están activos durante el test
- [ ] Plan de rollback si producción se ve accidentalmente impactada

## 5. Integración CI/CD

| Etapa | Tipo de Test | Condición de Gate |
|-------|--------------|-------------------|
| Pre-commit | Lint + unitarios | 100% pass, umbral de cobertura |
| Build de PR | Integración + contrato | 100% pass, sin nuevos tests flaky |
| Nightly | E2E + carga (línea base) | 100% pass, latencia dentro de 10% de línea base |
| Pre-release | Carga completa + remojo | 100% pass, SLOs cumplidos |
| Post-release | Smoke + sintéticos | 100% pass por 30 minutos |

## 6. Remediación de Tests Flaky

| Síntoma | Causa Probable | Solución |
|---------|----------------|----------|
| Fallos aleatorios a la misma hora diaria | Reset de base de staging compartida | Usar base de datos de test aislada |
| Timeouts en CI pero no localmente | CI más lento, falta retry | Aumentar timeout o mockear dependencia lenta |
| Pass/fail dependiente del orden | Efectos secundarios entre tests | Resetear estado antes de cada test |
| Heisenbug (desaparece al observarlo) | Race condition | Agregar sincronización o evitar estado mutable compartido |

Explicación

Los tests de contrato son el ciclo de retroalimentación más rápido para cambios de API porque se ejecutan en milisegundos y detectan inconsistencias de schema antes del despliegue. Los tests de integración verifican que tu código funciona con dependencias reales (base de datos, cola de mensajes) sin la sobrecarga de un suite E2E completo. Los tests de carga no se tratan de pasar un número; revelan fugas de recursos, agotamiento de conexiones y misconfiguraciones de autoscaling que solo aparecen bajo carga sostenida.

Escenario Detallado: Estrategia de Testing para API de Pedidos

API: POST /v1/orders (creacion de pedidos)
Equipos: Backend (proveedor), Mobile (consumidor), Web (consumidor)

Capa 1 - Tests de Contrato:
  Pact broker: https://pact.example.com
  Consumidor Mobile publica contrato:
    - Request: POST /v1/orders con body {customer_id, items[]}
    - Response: 201 con body {id, status, total_cents}
  Proveedor verifica en CI:
    $ pact-provider-verifier --provider-base-url=http://localhost:8080 \
      --pact-broker-url=https://pact.example.com \
      --provider=orders-api --consumer=mobile-app
  Gate: si el contrato falla, el PR no se mergea

Capa 2 - Tests de Integracion:
  Herramienta: Testcontainers + supertest
  Casos:
    - Happy path: pedido valido retorna 201
    - customer_id inexistente: retorna 400
    - items vacio: retorna 422
    - sin auth token: retorna 401
    - idempotency key repetida: retorna 409
  BD: PostgreSQL en Testcontainer, datos aislados por test

Capa 3 - Tests de Carga:
  Herramienta: k6
  Escenarios:
    - Baseline: 500 RPS por 10 min, p95 < 300ms
    - Spike: 5,000 RPS en 30s, sin errores 5xx
    - Soak: 300 RPS por 2h, memoria estable
  Frecuencia: semanal + pre-release

Capa 4 - E2E:
  Herramienta: Cypress
  Journey: login -> agregar al carrito -> checkout -> confirmar
  Frecuencia: nightly

Integracion CI/CD:
  Pre-commit: lint + unit tests (30s)
  PR build: integracion + contrato (3min)
  Nightly: E2E + carga baseline (20min)
  Pre-release: carga completa + soak (45min)

Como manejo tests de contrato cuando hay multiples consumidores?

Cada consumidor publica su propio contrato en el Pact Broker. El proveedor verifica todos los contratos en CI. Si un cambio rompe un consumidor pero no otros, el proveedor puede publicar una version nueva del contrato y notificar al consumidor afectado. Manten una matriz de compatibilidad: que consumidor usa que version del proveedor.

Deberia usar la misma base de datos de produccion para tests de integracion?

No. Usa Testcontainers (PostgreSQL en Docker) con la misma version y configuracion que produccion. Esto garantiza compatibilidad sin riesgo de mutar datos de produccion. Para datos de referencia estables (paises, monedas), usa fixtures versionados. Para datos transaccionales, usa factories que generen entidades frescas por test.

Variantes

ContextoEnfoqueNotas
ServerlessEnfatizar cold start + tests de concurrenciaLocalstack o ambiente dev en la nube
Event-driven / asyncTestear productor + consumidor por separadoUsar bus de mensajes de test, verificar consistencia eventual
Monolito legacyEnvolver API en tests de contrato antes de refactorizarPact con monolito como proveedor

Lo que funciona

  1. Tratar tests de contrato fallidos como fallos de build, no como advertencias
  2. Ejecutar tests de integración contra la misma versión de base de datos usada en producción
  3. Parametrizar tests de carga con distribuciones reales de requests, no tráfico uniforme
  4. Etiquetar tests flaky y repararlos dentro de un sprint; no dejarlos acumularse
  5. Almacenar resultados de tests y líneas base de rendimiento en una base de datos time-series para análisis de tendencias

Errores Comunes

  1. Testear solo el happy path y asumir que los errores “simplemente funcionan”
  2. Ejecutar tests de carga en ambientes que difieren de producción
  3. Usar datos de producción en tests sin anonimizar
  4. Permitir que tests dependan de servicios externos fuera de tu control
  5. Saltear tests de contrato porque “ambos equipos están en la misma sala”

Preguntas Frecuentes

¿Cuánta cobertura de test es suficiente?

100% de cobertura de tests de contrato. 80%+ de cobertura de integración para paths críticos. Test de carga para todos los endpoints que reciben > 1% del tráfico. Test E2E para los 3 user journeys principales. No persigas 100% de cobertura de líneas a expensas de aserciones significativas.

¿Debería mockear todos los servicios externos?

Mockea servicios que no poseas o controles (pasarelas de pago, APIs de terceros). Usa instancias reales para servicios que poseas que sean rápidos y confiables (servicio interno de auth, base de datos local). Esto da confianza sin flakiness.

¿Cuál es la diferencia entre tests de contrato e integración?

Los tests de contrato verifican que el schema de request/response coincide con el acuerdo entre consumidor y proveedor. Los tests de integración verifican que tu código de aplicación funciona correctamente con dependencias reales. Los tests de contrato son rápidos y enfocados en schema; los de integración son más lentos y enfocados en comportamiento.

End of document. Review and update quarterly.

Troubleshooting

  • Flaky tests: isolate shared state, time, and randomness. Make tests independent and deterministic; quarantine persistently flaky tests.
  • High coverage but bugs in production: coverage does not guarantee correctness. Add mutation testing, property-based tests, or contract tests.
  • Slow test suite: parallelize, mock slow dependencies, and avoid end-to-end tests for logic that can be unit tested.
  • Tests pass locally but fail in CI: check environment differences, timezone, locale, and dependency versions. Pin tool versions.
  • Debugging a failing integration test: log request/response payloads and use a dedicated test database. Reset state before each test.

Errores Comunes en Producción

  • Dejar campos requeridos vacíos o usar respuestas vagas de una palabra.
  • Llenar el documento una vez y nunca actualizarlo cuando cambia el alcance o las decisiones.
  • Guardar el documento donde el equipo no lo busque durante incidentes o revisiones.
  • No asignar un responsable, fecha límite o cadencia de revisión.
  • Copiar texto base sin eliminar secciones que no aplican.
  • Saltar el control de versiones, lo que impide rollback y responsabilidad.
  • No vincular el documento con decisiones relacionadas o acciones de seguimiento.
  • Evitar revisiones trimestrales que retirarían secciones obsoletas o sin uso.