---
name: calidad-contract-testing
description: >
  Contract testing: verificar que la implementación honra el contrato declarado
  en la spec (Pydantic, Zod, JSON Schema, OpenAPI/AsyncAPI, protobuf) y que
  consumidor y proveedor de una API no divergen. Cubre herramientas por stack
  (schemathesis, Dredd, Pact, zod, Pydantic, openapi-typescript), las dos
  familias de contract testing (schema-based property testing y
  consumer-driven contracts), generación de tests desde el schema del PLAN, y
  uso como gate de verificación. Cargar cuando el PLAN declara schemas como
  parte de la spec, al integrar dos servicios con un contrato compartido, o al
  detectar drift entre lo que la API documenta y lo que devuelve.
version: "1.0.0"
herramientasPermitidas: [Read, Bash, Grep, Glob]
exclusiones:
  - "No cargar para tests unitarios de lógica de negocio — eso es tdd-workflow; el contract testing verifica el límite spec↔implementación, no la lógica interna."
  - "No cargar si no hay un contrato declarado (schema, OpenAPI, Pact) — sin contrato no hay nada que verificar; primero declarar el schema en el PLAN."
  - "No cargar para validación de input en runtime — eso es responsabilidad del framework (Pydantic/Zod en el endpoint); el contract testing corre en CI, no en cada request."
evolvable: true
---
# Contract Testing — La Spec que se Verifica a Sí Misma

La cobertura responde "¿qué código ejecutan los tests?". El mutation testing,
"¿los tests detectarían un bug?". El contract testing responde la pregunta del
límite: **"¿la implementación honra el contrato que la spec promete?"**. Un
endpoint con 90% de cobertura puede devolver un campo con el tipo equivocado,
omitir uno requerido o aceptar un payload que el schema prohíbe — los tests
unitarios no lo ven porque usan los mismos supuestos que el código.

**Principio**: el schema declarado en el PLAN (Pydantic/Zod/JSON Schema/OpenAPI)
ES parte de la spec. Un contrato que no se verifica es documentación que miente.

## Cuándo cargar este skill

- El PLAN de la fase declara schemas (Pydantic, Zod, JSON Schema, OpenAPI) como
  entregable — esos schemas son contrato verificable, no decoración.
- Integración de dos servicios (frontend↔backend, microservicio↔microservicio)
  con un contrato compartido que ambos lados deben respetar.
- Drift detectado: la API documenta un campo que ya no devuelve, o devuelve uno
  no documentado; un cliente generado rompe tras un cambio de backend.
- Configurar contract testing como paso de `/swl:verificar` o gate de `tdd-qa-swl`.

## Las dos familias de contract testing

| Familia | Pregunta | Cuándo |
|---------|----------|--------|
| **Schema-based / property testing** | ¿La API respeta su propio schema OpenAPI ante cualquier input válido? | API con spec OpenAPI; un solo equipo controla ambos lados |
| **Consumer-driven contracts (CDC)** | ¿El proveedor sigue cumpliendo lo que cada consumidor concreto espera? | Múltiples consumidores, equipos separados, despliegue independiente |

Schema-based es más barato (un solo artefacto, la spec) y cubre el 80% del valor
en proyectos de un equipo. CDC paga su complejidad cuando hay equipos y
despliegues independientes que pueden romperse mutuamente sin saberlo.

## Cómo funciona (schema-based)

1. El schema (OpenAPI/Pydantic/Zod) define el contrato: rutas, métodos, forma de
   request y response, campos requeridos, tipos, constraints.
2. La herramienta **genera casos** desde el schema (property-based: cientos de
   payloads válidos e inválidos) y los lanza contra la API real.
3. Verifica que cada response **conforme al schema**: status esperado, forma,
   tipos, requeridos presentes, sin campos extra prohibidos.
4. Reporta violaciones: el contrato dice X, la implementación devolvió Y.

## Herramientas por stack

Antes de instalar, verificar versión vigente con Context7
(regla `usar-context7.md`) — los nombres de paquete cambian entre majors.

| Stack | Herramienta | Familia | Comando típico |
|-------|------------|---------|----------------|
| OpenAPI (cualquier lenguaje) | `schemathesis` | schema-based | `schemathesis run http://localhost:8000/openapi.json` |
| OpenAPI (Node) | Dredd | schema-based | `dredd openapi.yaml http://localhost:3000` |
| Python | Pydantic v2 (`model_validate`) | schema (límite) | validar request/response contra el modelo en el test |
| JS/TS | Zod (`.parse`/`.safeParse`) | schema (límite) | parsear la response contra el schema en el test |
| TS desde OpenAPI | `openapi-typescript` + tsc | schema (compile-time) | generar tipos y dejar que el compilador detecte drift |
| Multi-equipo (poliglota) | Pact (`@pact-foundation/pact`, `pact-python`) | CDC | consumer genera pacto → provider lo verifica |
| gRPC | `buf breaking` / protovalidate | schema-based | `buf breaking --against '.git#branch=main'` |

## Generar tests desde el schema del PLAN

Cuando `planear-fase` declara un schema como entregable, la tarea que lo
implementa se verifica generando el contract test, no escribiéndolo a mano:

```
# OpenAPI + FastAPI: schemathesis deriva los casos del propio /openapi.json
schemathesis run http://localhost:8000/openapi.json --checks all
```

```python
# Pydantic como contrato en el test de integración:
def test_endpoint_respeta_contrato():
    # verifica: REQ-NN
    r = client.get("/facturas/1")
    FacturaResponse.model_validate(r.json())  # falla si la forma no conforma
```

```typescript
// Zod como contrato del lado consumidor:
const FacturaSchema = z.object({ id: z.number(), total: z.number(), vigencia: z.string() });
const data = FacturaSchema.parse(await res.json()); // throw si el backend cambió la forma
```

## Umbrales y cuándo aplica el gate

| Contexto | Gate | Justificación |
|----------|------|---------------|
| API pública o consumida por otro equipo | obligatorio: 0 violaciones de contrato | Un drift rompe consumidores en silencio |
| Integración interna front↔back del mismo proyecto | recomendado: schema-based en CI | Barato con OpenAPI ya existente; atrapa el campo renombrado |
| Endpoint interno sin consumidores externos | opcional | El esfuerzo de mantener pactos no paga |

No imponer CDC donde schema-based basta: Pact con broker es infraestructura que
solo paga con equipos y despliegues independientes.

## Uso como gate de verificación

En `/swl:verificar`, tras los tests unitarios: si la fase declaró schemas en el
PLAN, correr el contract test contra la API real (entorno de test) y tratar cada
violación de contrato como hallazgo CRÍTICO — la implementación contradice la
spec aprobada. En `tdd-qa-swl` es gate opt-in: requiere la API levantable en CI.

## Cuándo NO cargar

- No hay contrato declarado (ni OpenAPI, ni schema, ni Pact) — primero declarar
  el schema en el PLAN; sin contrato el contract testing no tiene qué verificar.
- Lógica de negocio pura sin límite de API — eso es `tdd-workflow`/`testing-*`.
- Prototipo de descarte donde la API cambia cada hora — el contrato se
  estabiliza primero, luego se verifica.

## Gotchas / Errores comunes no obvios

- **Schema permisivo da falso verde**: un OpenAPI con `additionalProperties: true`
  y casi todo opcional "conforma" con cualquier response — el contract test pasa
  sin verificar nada. Causa: schema generado laxo (FastAPI sin `model_config`
  estricto, Zod con `.passthrough()`). Solución: el contrato debe ser tan
  estricto como la promesa real — requeridos marcados, `additionalProperties:
  false` donde aplica.
- **Property testing encuentra "bugs" en el schema, no en el código**: schemathesis
  genera un edge case válido por schema que el código nunca contempló (string de
  10000 chars, número en el límite). A veces el bug es del schema (demasiado
  permisivo), no del endpoint. Diagnosticar antes de "arreglar" el código.
- **Pact sin broker compartido es teatro**: si consumer y provider verifican
  contra pactos en ramas distintas sin un broker central que versione, cada lado
  pasa en aislamiento y rompen juntos en producción. CDC sin broker = falsa
  seguridad; usar schema-based si no hay broker.
- **Tipos generados que nadie recompila**: `openapi-typescript` genera tipos al
  día del commit; si el pipeline no los regenera contra la spec viva, el
  compilador valida contra un contrato fósil. El gate debe regenerar y comparar,
  no confiar en el archivo commiteado (regla `verificar-citas-normativas.md §
  comentarios temporales`).
- **Contract test que levanta la app real es lento y flaky en CI**: si requiere
  BD y servicios, hereda toda su fragilidad. Para schema-based puro, preferir el
  modo que valida la spec estáticamente o contra un mock conformante antes de
  exigir la app completa levantada.

## Anti-patrones

- **Schema decorativo**: declarar OpenAPI/Pydantic y nunca verificar que la
  implementación lo cumple — documentación que diverge en silencio.
- **CDC donde basta schema-based**: montar Pact + broker para un front↔back de
  un solo equipo; complejidad sin retorno.
- **Verificar el contrato contra un mock que usa el mismo schema**: tautología —
  el mock conforma por construcción; el contract test debe correr contra la
  implementación real.
- **Tratar la violación de contrato como warning**: si la API rompe su contrato,
  un consumidor ya está roto; es CRÍTICO, no observación.
