API y empresas

Tests de contrato de la API de multas: cambios incompatibles

Un campo opcional nuevo suele ser compatible; un enum cerrado nuevo puede romper un cliente estricto. Los fixtures de consumidor convierten esas diferencias en pruebas antes del despliegue.

Por Marco Ferreiro ·

Conectores físicos sometidos a pruebas de compatibilidad en dos paneles

Dos respuestas pueden ser válidas para el servidor y no para el cliente. Un proveedor agrega un estado de acta, el schema lo acepta y el consumidor generado lanza una excepción porque su enum quedó cerrado. El contrato útil no termina en el archivo OpenAPI: incluye lo que cada integración realmente puede leer y hacer.

Los tests de contrato convierten esas expectativas en ejemplos ejecutables. Usan patentes sintéticas y se ejecutan antes de integrar una nueva especificación o desplegar un cambio.

Tres niveles de compatibilidad

Primero validamos estructura: tipos, campos requeridos, formatos y variantes según OpenAPI y JSON Schema. Después validamos transporte: status, headers, media type y semántica HTTP. Por último probamos comportamiento: qué muestra o almacena el consumidor.

Un cambio puede pasar el primer nivel y fallar el tercero. Por ejemplo, el status: corrected de un acta puede ser un string válido si el schema es abierto, pero un switch del cliente sin caso por defecto puede tratarlo como completed o fallar.

La suite debe informar en qué nivel apareció el problema, no sólo un resultado rojo.

Congelar productor y consumidor

Cada ejecución registra versión del OpenAPI, revisión del JSON Schema, cliente generado o biblioteca, configuración de deserialización y commit de los fixtures. Comparar “último contra último” vuelve imposible reproducir un fallo pasado.

El contrato del productor describe lo que puede emitir. El contrato del consumidor expresa el subconjunto y las tolerancias que necesita. La prueba de compatibilidad evalúa ambos, sin obligar a que todos los clientes actualicen al mismo tiempo.

OpenAPI 3.1 adopta el modelo de JSON Schema 2020-12 con particularidades de su dialecto. La herramienta debe conocer qué versión interpreta; usar otro draft puede cambiar validación de referencias o palabras clave.

El catálogo mínimo de fixtures

Para consultas de infracciones incluimos, como mínimo:

Los identificadores usan dominios de ejemplo y valores ficticios. No hace falta copiar una patente real para probar un parser.

Cambios aditivos que pueden romper

Agregar una propiedad opcional suele ser compatible si los clientes ignoran propiedades desconocidas. Sin embargo, algunos deserializadores usan una política estricta. Por eso mantenemos dos consumidores sintéticos: uno tolerante y otro que reproduce esa rigidez.

Agregar un valor a un enum también parece aditivo desde el productor, pero es riesgoso para código generado. El cliente debería conservar el estado de acta desconocido o mapearlo a una categoría explícita, nunca reinterpretarlo como el primer caso.

Agregar un nuevo subtipo en oneOf puede afectar discriminadores. Cambiar el orden de propiedades no debería importar en JSON, pero un test puede descubrir código casero que depende de él.

Cambios claramente restrictivos

Volver obligatorio un campo opcional, reducir un máximo, quitar una variante o cambiar string por integer restringe instancias previamente válidas. Renombrar una propiedad es quitar y agregar, no una modificación inocua.

Cambiar null por ausencia también puede ser incompatible. JSON Schema distingue una propiedad no presente de una presente con valor nulo. El modelo de infracciones debe definir qué significa cada caso.

Los tests generan ejemplos que eran válidos antes y verifican si dejan de serlo. El diff del documento ayuda, pero el ejemplo muestra el impacto concreto.

HTTP forma parte del contrato

Una respuesta 200 con un objeto de error puede satisfacer un schema demasiado amplio y romper la semántica del cliente. RFC 9110 define significados para métodos y códigos; el contrato debe usar respuestas diferenciadas y schemas específicos.

También probamos Content-Type, Location, ETag y Retry-After cuando son parte del flujo. Un cuerpo JSON válido servido como HTML puede fallar antes de deserializar. Un 304 no lleva la representación habitual y el cliente debe conservar la copia correcta.

Los reintentos se prueban sin consultar portales reales: un servidor fixture devuelve una secuencia controlada.

Probar comportamiento, no sólo parsing

Después de deserializar, el consumidor ejecuta invariantes. Si una fuente está failed, el resumen no puede decir “sin multas”. Si el resultado está partial, debe conservar qué jurisdicciones respondieron. Si llega una corrección con revisión mayor, no se duplica la infracción.

Estas reglas capturan el riesgo de negocio que el schema no conoce. Dos objetos estructuralmente válidos pueden hacer que una flota vea deuda donde no la hay.

La prueba verifica también telemetría: no deben enviarse patentes, títulos completos o cuerpos sensibles en logs de error.

Matriz contra dos clientes

El experimento ejecuta cada fixture contra un cliente tolerante y uno estricto. Para cada cambio registramos: validación de schema, parsing, resultado de dominio y mensaje mostrado. Luego barajamos campos, agregamos propiedades y variamos enums de forma controlada.

El objetivo no es demostrar que el tolerante siempre es mejor. Rechazar desconocidos puede detectar errores internos, pero dificulta evolución. La decisión debe ser consciente y visible en la matriz.

Cuando un cliente falla, el caso se transforma en fixture permanente. Así la corrección no desaparece en una conversación.

La puerta de integración

En CI se valida primero el OpenAPI, luego se genera el diff y por último corre la suite de consumidor. Un cambio incompatible requiere nueva versión o un plan de convivencia; no se autoriza sólo porque el generador pudo compilar.

La puerta produce un artefacto legible: cambio, flotas afectadas, fixture, resultado y decisión. Los falsos positivos se corrigen ajustando expectativas versionadas, no desactivando toda la suite.

Lo que la suite no reemplaza

Los tests no conocen consumidores que nunca declararon su uso, ni garantizan que una red o credencial funcione. Tampoco sustituyen documentación de migración, deprecación y métricas en producción.

Su valor es acotar lo desconocido. Cuando respuestas completas, parciales y corregidas ya existen como contratos ejecutables, una API de infracciones puede evolucionar sin usar a cada flota como detector de incompatibilidades.

Metodología

Alcance
Matriz de compatibilidad de una API de consultas, ejecutada contra dos consumidores sintéticos con políticas tolerante y estricta.
Unidad de análisis
Un cambio de contrato y el resultado de validación, deserialización y comportamiento observado en cada cliente.
Cobertura
OpenAPI 3.1.2, JSON Schema 2020-12 y semántica HTTP verificadas al 14 de agosto de 2026.

Limitaciones

Fuentes

Preguntas frecuentes

¿Agregar un campo opcional siempre es compatible?

Para un consumidor que ignora campos desconocidos suele ser aditivo, pero un deserializador configurado para rechazarlos puede romperse. El test debe representar al cliente real, no sólo al schema ideal.

¿Validar el OpenAPI alcanza como test de contrato?

No. Detecta incoherencias estructurales, pero no prueba decisiones de negocio, tratamiento de estados parciales ni compatibilidad del código generado y configurado por cada consumidor.

¿Conviene probar con respuestas de producción?

No es necesario y puede exponer datos. Los fixtures deben ser sintéticos, mínimos y diseñados para cubrir variantes; cualquier caso derivado de producción necesita anonimización y autorización específica.

Notas relacionadas