Una API no queda definida sólo por su controlador PHP ni por una especificación publicada. También la definen las expectativas que otros sistemas ya incorporaron: una ruta, el nombre de un campo, un código HTTP, el formato de un error o el orden necesario para recorrer páginas. Las pruebas de contrato para APIs PHP convierten esas expectativas en verificaciones automatizadas antes de fusionar código o desplegarlo.
El objetivo no es impedir toda evolución. Es detectar si un cambio altera un acuerdo observable por un consumidor y decidirlo conscientemente: mantener compatibilidad, introducir una transición o versionar la interfaz. Esto resulta especialmente importante en APIs internas con varios equipos, integraciones B2B y flujos asíncronos donde el fallo puede aparecer horas después de la publicación.
Qué resuelven las pruebas de contrato y qué no sustituyen

Una prueba de contrato comprueba que proveedor y consumidor coinciden en una interacción: dada una solicitud válida, el proveedor produce una respuesta con una estructura, tipos y reglas acordadas. En sentido inverso, un consumidor puede declarar qué solicitudes necesita y el proveedor verifica que puede atenderlas.
Este enfoque detecta incompatibilidades que las pruebas unitarias suelen pasar por alto. Un test unitario puede confirmar que un serializador devuelve customer_id; no demuestra que el consumidor siga entendiendo ese campo si antes esperaba customerId. Una prueba de integración local puede cubrir el endpoint, pero no necesariamente recoge los supuestos reales de cada integración.
No reemplazan otros controles:
- Pruebas unitarias, para reglas de dominio, validación y transformaciones.
- Pruebas de integración, para base de datos, colas, caché, autenticación o servicios conectados.
- Pruebas end-to-end, para recorridos críticos completos en entornos controlados.
- Pruebas de seguridad y rendimiento, para autorización, abuso, exposición de datos, latencia y capacidad.
- Observabilidad en producción, para detectar consumidores que aún usan comportamientos en retirada.
Un contrato tampoco certifica que la respuesta sea correcta para el negocio; certifica que conserva la forma y semántica que se han declarado. Por ello, debe acompañarse de ejemplos que expresen reglas relevantes, no sólo esquemas vacíos.
Qué forma parte del contrato de una API
El contrato es cualquier comportamiento que un consumidor pueda observar y del que dependa. Limitarlo al JSON de una respuesta exitosa deja fuera las roturas más frecuentes. Para cada operación conviene acordar como mínimo los siguientes elementos.
- Solicitud: método, ruta, parámetros de consulta, cabeceras, cuerpo, campos obligatorios, formatos y límites.
- Respuesta: código HTTP, cabeceras relevantes, estructura, tipos, campos opcionales, campos que admiten valores nulos y formatos de fecha, moneda o identificadores.
- Errores: códigos de estado, cuerpo de error, código funcional estable y condiciones que lo generan. Un texto para personas puede cambiar; un código como
validation_failedes más apto para automatización. - Paginación y filtrado: significado de
limit, cursor o página, estabilidad del orden, representación del siguiente cursor y tratamiento de conjuntos vacíos. - Autenticación y autorización: mecanismo admitido, cabeceras exigidas, ámbitos o permisos, y diferencia entre credenciales inválidas, ausencia de credenciales y acceso denegado.
- Eventos y webhooks: nombre del evento, versión o esquema del payload, firma, reintentos, identificador de evento, orden no garantizado y expectativas de idempotencia.
La obligatoriedad de una propiedad y la admisión de null son reglas independientes. Un campo puede ser obligatorio y admitir null, opcional y no admitirlo cuando aparece, u opcional y admitirlo si está presente. Asimismo, una propiedad omitida, una propiedad presente con null y una propiedad con cadena vacía son estados distintos. Si el consumidor los interpreta de forma diferente, el contrato debe expresarlo y probarlo.
Cambios pequeños que pueden romper consumidores
Una modificación puede parecer inocua desde el proveedor y ser incompatible para un cliente generado, un validador estricto o una lógica de negocio. Cambiar un entero por una cadena, por ejemplo 42 a "42", rompe comparaciones y esquemas. Hacer un campo opcional no determina por sí mismo si acepta null: la primera regla define si la propiedad debe estar presente, mientras que la segunda define los valores válidos cuando se incluye.
Otros cambios de riesgo son devolver 200 donde antes se devolvía 201, sustituir una lista vacía por null, modificar la precisión de un decimal, renombrar un código de error o cambiar el orden de paginación sin avisar. Agregar un campo suele ser compatible para lectores tolerantes, pero no lo es si un consumidor valida un esquema cerrado o calcula firmas sobre el cuerpo completo.
La compatibilidad depende del acuerdo real, no de una regla aislada. Conviene clasificar cada cambio según consumidores conocidos, tolerancia declarada y criticidad del flujo. Si no se conoce esa información, debe tratarse como un riesgo y no como una suposición favorable.
Elegir entre especificación, contratos de consumidor o ambos
Una especificación de interfaz, por ejemplo una descripción OpenAPI, funciona bien como fuente común para rutas, operaciones, parámetros, esquemas y respuestas. Puede validarse en la canalización para detectar cambios incompatibles frente a una versión de referencia. Es útil cuando hay muchos consumidores o se generan clientes y documentación a partir de la misma definición.
Sin embargo, un esquema no siempre captura lo que importa a cada consumidor: combinaciones de filtros, un error específico ante una condición de negocio o una dependencia sobre un valor de ejemplo. Los contratos dirigidos por consumidores declaran interacciones concretas que cada consumidor necesita. El proveedor los verifica contra su implementación.
Usar ambos niveles suele ser razonable: la especificación gobierna la superficie general y los contratos de consumidor cubren flujos de alto valor o semántica difícil de reducir a un esquema. Debe existir una responsabilidad clara sobre cada artefacto. Si una especificación no se actualiza al cambiar el código, deja de ser una fuente de verdad y pasa a ser documentación ficticia.
Ejemplos representativos y casos límite
Un ejemplo de contrato debe contener datos realistas en estructura, no datos de producción. Para un recurso de pedido, incluya un caso con elementos, uno vacío si es válido, identificadores con el formato acordado y fechas completas con zona horaria cuando esa sea la convención. Añada casos de autorización denegada, validación fallida, recurso inexistente y paginación final.
Evite fijar detalles irrelevantes que cambian legítimamente, como un identificador aleatorio, la hora actual o el orden de propiedades JSON. Use aserciones precisas sobre lo estable y tolerancia explícita sobre lo variable. Cada ejemplo debe responder a una necesidad conocida; una colección enorme de respuestas inventadas aumenta mantenimiento sin elevar la confianza.
Implementación progresiva en una API PHP existente
No es necesario modelar toda la API antes de obtener valor. Empiece con un inventario de consumidores: aplicaciones internas, clientes B2B, procesos batch, aplicaciones móviles, automatizaciones y receptores de webhooks. Registre propietario, canal de contacto, operación utilizada, criticidad y capacidad de actualización.
Después priorice endpoints que crean o modifican recursos, autentican usuarios, alimentan procesos financieros o activan automatizaciones. Establezca una línea base de su comportamiento actual mediante una especificación revisada y pruebas contra una instancia reproducible de la API. En PHP, la prueba debe ejercitar la capa HTTP real de la aplicación, no invocar directamente una clase de servicio: el contrato incluye routing, middleware, serialización y manejo de excepciones.
POST /api/orders
Authorization: Bearer token
Content-Type: application/json
{"items":[{"sku":"ABC-1","quantity":2}]}
201 Created
{"id":"ord_123","status":"pending","items":[...]}El ejemplo anterior sólo es útil si se acompaña de reglas: qué campos son obligatorios, si id siempre es cadena, qué errores devuelve un SKU inválido y si el estado inicial está garantizado. Esas reglas son las que deben convertirse en aserciones.
Validación en integración continua y antes del despliegue
La canalización debe fallar antes de fusionar si la implementación incumple contratos aprobados. Un flujo práctico incluye ejecutar pruebas unitarias, levantar dependencias controladas, iniciar la API PHP con configuración de prueba y validar especificación, contratos de proveedor y contratos de consumidores representativos. Las pruebas deben usar datos aislados y deterministas para que un fallo sea reproducible.
En una solicitud de cambio, compare además la especificación propuesta con la versión publicada para señalar eliminaciones de rutas, endurecimiento de requisitos de presencia, cambios en la admisión de null, cambios de tipo y respuestas eliminadas. El diagnóstico debe indicar operación, interacción y regla incumplida; un simple error de esquema obliga a investigar demasiado.
Antes del despliegue, ejecute la misma suite sobre el artefacto que se va a publicar, no sobre una compilación distinta. Tras desplegar, supervise códigos de error, fallos de deserialización reportados por clientes, uso de versiones y tráfico hacia rutas deprecadas. La validación previa reduce riesgo; no sustituye confirmar el comportamiento bajo tráfico real.
Compatibilidad, deprecación y retirada segura

Cuando un cambio no sea compatible, prefiera una transición explícita. Puede añadir un nuevo campo mientras conserva el anterior, introducir una nueva operación o versión, y comunicar una fecha de retirada respaldada por señales de uso. La deprecación es un periodo operativo con propietarios, comunicación y medición; no sólo una nota en la documentación.
No retire un comportamiento porque haya pasado un plazo si no puede identificar consumidores pendientes o si el flujo es crítico. Cuando sea posible, exponga advertencias controladas y métricas para localizar uso antiguo sin alterar la respuesta. La exposición gradual de una nueva versión permite observar errores y corregir contratos antes de extender el cambio.
Los errores más habituales son probar únicamente respuestas exitosas, modelar mensajes humanos en lugar de códigos de error, asumir que todos los clientes ignoran campos nuevos y no involucrar a consumidores reales o representativos. Las pruebas de contrato aportan valor cuando reflejan acuerdos mantenidos por ambas partes y se ejecutan como una condición normal de entrega.



