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 ·
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:
- respuesta completa con todas las fuentes terminadas;
- respuesta parcial con una fuente fallida;
- consulta sin actas, pero con cobertura explícita;
- corrección de un resultado anterior;
- campo opcional desconocido;
- valor nuevo de un enum;
- campo documentado como nullable;
- lista vacía y campo ausente cuando el contrato los diferencia;
- error HTTP estructurado;
429o indisponibilidad conRetry-After.
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
- La clasificación compatible o incompatible depende de cómo usan el campo los consumidores reales.
- La suite reduce riesgo, pero no reemplaza versionado, comunicación ni despliegue gradual.
Fuentes
- OpenAPI Specification 3.1.2 — OpenAPI Initiative (consultada el )
- JSON Schema Core 2020-12 — JSON Schema (consultada el )
- JSON Schema Validation 2020-12 — JSON Schema (consultada el )
- RFC 9110 — HTTP Semantics — RFC Editor (consultada el )
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
Cómo versionar una respuesta cuando cada jurisdicción cambia
Una respuesta agregada necesita distinguir versión de contrato, revisión de datos y estado de cada fuente. Así un cambio municipal no obliga a reinterpretar silenciosamente todo el resultado.
Cobertura de una API: cómo comunicar fuentes caídas y resultados parciales
Una integración de infracciones necesita informar qué fuentes respondió, cuáles fallaron y si el resultado es completo. Un cero sin cobertura comprobada puede inducir decisiones incorrectas.
Timeout no significa “sin multas”: cómo informar resultados parciales en una integración
Cuando una fuente no responde, el resultado es parcial o indeterminado, nunca cero. Un contrato explícito permite reintentar, alertar y decidir sin producir falsos negativos.