19. Pruebas de contratos entre productores y consumidores

19.1 Introducción

Cuando dos componentes se comunican, existe un acuerdo entre ellos. Uno produce información o comportamiento, y otro lo consume. Si ese acuerdo cambia sin control, la integración puede romperse aunque cada componente funcione correctamente de forma aislada.

Las pruebas de contratos ayudan a verificar que productores y consumidores sigan respetando esos acuerdos. Son especialmente útiles en sistemas con servicios internos, APIs, eventos, colas o módulos mantenidos por equipos distintos.

En este tema veremos qué son estas pruebas, qué verifican y cómo se relacionan con las pruebas de integración.

19.2 Productores y consumidores

En una integración, el productor es el componente que entrega datos, eventos, respuestas o comportamiento. El consumidor es el componente que usa eso que el productor entrega.

Ejemplos:

  • Una API de usuarios produce datos; una aplicación web los consume.
  • Un servicio de órdenes publica un evento; un servicio de notificaciones lo consume.
  • Una base de datos produce resultados de consulta; un repositorio los consume.
  • Un módulo de permisos responde si una acción está autorizada; otro módulo consume esa decisión.

La integración funciona mientras productor y consumidor compartan la misma expectativa.

19.3 Qué es un contrato

Un contrato define qué puede esperar un consumidor del productor. Puede estar escrito en documentación formal, especificaciones, esquemas, ejemplos o pruebas.

Un contrato puede incluir:

  • Campos obligatorios y opcionales.
  • Tipos de datos.
  • Formatos de fechas, importes o identificadores.
  • Códigos de estado.
  • Mensajes de error.
  • Estructura de eventos.
  • Reglas sobre valores permitidos.
Un contrato no describe todo el sistema. Describe el acuerdo necesario para que productor y consumidor puedan colaborar.

19.4 Qué verifican las pruebas de contratos

Las pruebas de contratos verifican que un productor y un consumidor sigan siendo compatibles.

Pueden comprobar:

  • Que el productor entregue los campos que el consumidor necesita.
  • Que los tipos de datos sean los esperados.
  • Que los errores se representen como el consumidor sabe manejarlos.
  • Que un evento publicado tenga la estructura acordada.
  • Que un cambio en el productor no rompa consumidores existentes.
  • Que el consumidor no dependa de detalles que no forman parte del contrato.

19.5 Diferencia con una prueba de integración común

Una prueba de integración común puede ejecutar productor y consumidor juntos para verificar un flujo. Una prueba de contrato se concentra específicamente en el acuerdo entre ambos.

Tipo de prueba Foco principal Ejemplo
Integración común Colaboración en un escenario concreto. Crear una orden y enviar notificación.
Contrato Compatibilidad entre productor y consumidor. El evento de orden creada contiene los campos que notificaciones necesita.

Ambas son complementarias. Las pruebas de contrato reducen el riesgo de incompatibilidades antes de ejecutar flujos más amplios.

19.6 Contratos en APIs

En APIs, el contrato suele incluir rutas, métodos, parámetros, encabezados, cuerpos de solicitud, cuerpos de respuesta y códigos de estado.

Por ejemplo, un consumidor puede esperar que la API de usuarios devuelva:

  • id como identificador.
  • nombre como texto.
  • email como texto obligatorio.
  • estado con valores conocidos.
  • Un código de error específico si el usuario no existe.

Si el productor cambia email por correo sin coordinar, el consumidor puede romperse.

19.7 Contratos en eventos y colas

En sistemas asíncronos, el contrato puede estar en la estructura de un evento o mensaje. El consumidor no llama al productor directamente, pero depende de lo que el productor publica.

Un evento de orden creada podría incluir:

  • Identificador de orden.
  • Identificador de cliente.
  • Fecha de creación.
  • Total.
  • Estado inicial.
  • Versión del evento.

Las pruebas de contrato pueden verificar que el productor publique eventos con la estructura que los consumidores esperan.

19.8 Contratos de error

Los errores también forman parte del contrato. Un consumidor necesita saber cómo interpretar fallas conocidas.

Conviene definir y probar:

  • Error por recurso no encontrado.
  • Error por datos inválidos.
  • Error por credenciales insuficientes.
  • Error temporal.
  • Formato del mensaje de error.
  • Códigos o identificadores de error.

Si el productor cambia la forma de reportar errores, el consumidor puede dejar de manejar casos importantes.

19.9 Contratos impulsados por consumidores

En algunos equipos se usa el enfoque de contratos impulsados por consumidores. La idea es que los consumidores expresan qué necesitan del productor. Luego el productor verifica que puede cumplir esas expectativas.

Esto ayuda a evitar que el productor cambie una respuesta pensando que nadie la usa, cuando en realidad un consumidor depende de ciertos campos.

Este enfoque es útil cuando:

  • Hay varios consumidores para un mismo productor.
  • Los servicios son mantenidos por equipos distintos.
  • Los despliegues no siempre ocurren al mismo tiempo.
  • Los cambios de contrato son frecuentes.

19.10 Cambios compatibles e incompatibles

No todos los cambios de contrato tienen el mismo riesgo. Algunos son compatibles y otros pueden romper consumidores.

Cambio Riesgo Comentario
Agregar un campo opcional. Bajo Suele ser compatible si los consumidores ignoran campos desconocidos.
Eliminar un campo usado. Alto Puede romper consumidores inmediatamente.
Cambiar tipo de dato. Alto Un número como texto puede generar errores de interpretación.
Agregar un nuevo valor de estado. Medio o alto Depende de si el consumidor maneja valores desconocidos.
Cambiar formato de error. Alto Puede impedir que el consumidor responda adecuadamente.

19.11 Versionado de contratos

Cuando un contrato necesita cambiar de forma incompatible, puede ser necesario versionarlo. El versionado permite que consumidores antiguos sigan usando una versión mientras migran a otra.

Puede versionarse:

  • Una ruta de API.
  • Un esquema de evento.
  • Un formato de archivo.
  • Un conjunto de campos de respuesta.

Las pruebas de contrato ayudan a verificar que cada versión siga cumpliendo lo prometido a sus consumidores.

19.12 Qué no debe asumir el consumidor

Un consumidor no debería depender de detalles que no forman parte del contrato. Si lo hace, se vuelve frágil.

Ejemplos de malas dependencias:

  • Depender del orden de campos si el contrato no lo garantiza.
  • Usar un campo interno no documentado.
  • Asumir que nunca aparecerán nuevos valores de estado.
  • Interpretar texto de error libre como si fuera un código estable.
  • Depender de tiempos exactos de respuesta.

Las pruebas de contrato también pueden revelar expectativas incorrectas del consumidor.

19.13 Ejemplo: contrato de usuario

Supongamos que una aplicación consume datos de un servicio de usuarios. El contrato mínimo podría ser:

Campo Tipo Uso del consumidor
id Texto o número según contrato. Identificar al usuario en operaciones posteriores.
nombre Texto. Mostrar el nombre en pantalla.
email Texto. Enviar notificaciones.
estado Texto con valores conocidos. Determinar si puede operar.

Una prueba de contrato puede comprobar que el productor sigue entregando esos campos con el tipo y significado esperados.

19.14 Ejemplo: contrato de evento

Supongamos un evento orden_creada. El consumidor de notificaciones necesita saber a quién avisar y qué operación ocurrió.

El contrato podría indicar que el evento debe incluir:

  • eventId: identificador único del evento.
  • ordenId: identificador de la orden.
  • clienteId: identificador del cliente.
  • email: contacto para notificación.
  • total: importe de la orden.
  • version: versión del contrato del evento.

Si el productor deja de enviar email, el consumidor no podrá enviar la notificación aunque el evento siga publicándose.

19.15 Errores comunes

Al trabajar con contratos, suelen aparecer errores como:

  • Confiar solo en documentación que no se verifica automáticamente.
  • Agregar cambios incompatibles sin avisar a consumidores.
  • No probar errores y estados alternativos.
  • Simular respuestas distintas de las reales.
  • No saber qué consumidores usan cada campo.
  • Depender de detalles no documentados.
  • No versionar contratos cuando hay cambios incompatibles.

19.16 Lista de verificación

Para revisar una prueba de contrato, podemos preguntar:

  • ¿Está claro quién es productor y quién es consumidor?
  • ¿El contrato define campos obligatorios, tipos y formatos?
  • ¿Se verifican respuestas exitosas y errores importantes?
  • ¿El consumidor evita depender de detalles no garantizados?
  • ¿Los cambios incompatibles están identificados?
  • ¿Existe una estrategia de versionado si hace falta?
  • ¿Las pruebas se ejecutan cuando cambia productor o consumidor?

19.17 Qué debes recordar de este tema

  • Las pruebas de contratos verifican acuerdos entre productores y consumidores.
  • Un contrato puede aplicarse a APIs, eventos, archivos, módulos o servicios.
  • Los errores también forman parte del contrato.
  • Los contratos ayudan a detectar cambios incompatibles antes de romper integraciones reales.
  • El consumidor no debería depender de detalles fuera del contrato.
  • El versionado ayuda cuando un contrato debe cambiar de forma incompatible.

19.18 Conclusión

Las pruebas de contratos son una herramienta poderosa para reducir fallas de integración entre componentes que evolucionan. Ayudan a que productores y consumidores mantengan acuerdos claros y verificables.

Estas pruebas no reemplazan todos los escenarios de integración, pero permiten detectar rupturas de compatibilidad antes de ejecutar flujos más amplios.

En el próximo tema veremos la integración con colas, eventos y procesos asíncronos.