Pruebas de contrato en microservicios financieros

Cuando en fintech.works empezamos a usar pruebas de contrato, al principio lo vimos más como una molestia que como una mejora real para nuestro proceso de desarrollo, sobre todo dentro del CI/CD.
En ese momento lo percibíamos como un paso “extra”: un poco más de documentación, pero mucho más trabajo. Sin embargo, a medida que fuimos extendiendo su uso a todos nuestros sistemas, nos dimos cuenta de que no era un paso adicional sino una herramienta muy valiosa que aportaba orden, confianza y claridad en el desarrollo.
En esta publicación queremos contar nuestra experiencia aplicando pruebas de contrato y las mejoras que observamos a medida que ganaron protagonismo en nuestro día a día.
Primeros pasos con contratos
En 2024 trabajamos con un cliente que estaba reorganizando sus procesos de desarrollo. Como suele pasar en empresas que crecen muy rápido, los procesos que alguna vez funcionaron quedaron obsoletos cuando la cantidad de proyectos se multiplicó.
Lo que antes era un entorno ágil terminó convertido en un espagueti de repositorios, tecnologías, cuentas cloud y proyectos. Cambiar o agregar funcionalidades se volvió cada vez más difícil, y hasta el onboarding de nuevos colaboradores era un desafío por la cantidad de sistemas interconectados que tenían que aprender.
Una de las medidas para recuperar control fue exigir que cada equipo mantuviera un contrato válido de sus servicios.
Qué son los contratos
Un contrato define cómo dos servicios deben comunicarse.
Por ejemplo, en un sistema de pagos:
- Pagos: ejecuta un pago a una cuenta, pero antes consulta al servicio de Autorización.
- Autorización: valida la transacción con base en reglas predefinidas.
Ese acuerdo formal (qué datos se envían, en qué formato, qué respuestas son válidas) es lo que llamamos contrato.
Supongamos que el servicio de pagos manda la siguiente petición HTTP y espera recibir una respuesta del servicio de autorización, esa interacción la podemos definir en un JSON como el siguiente incluyendo los detalles como el body que se debe enviar, el código de respuesta y el body del mismo, este JSON se ve de esta forma:
{
"request": {
"method": "POST",
"path": "/transactions",
"body": { "amount": 100, "currency": "PYG" }
},
"response": {
"status": 201,
"body": { "transactionId": "abc123", "status": "APPROVED" }
}
}
A este JSON se lo puede considerar como un contrato, por ahora informal, de la interacción entre los microservicios.
Ahora la definición formal nos dice que un contrato es un acuerdo entre dos servicios, sobre las expectativas que tiene cada uno a la hora de comunicarse entre sí, como todos nuestros servicios son APIs, podemos categorizar a cada servicio como "consumidor" o "proveedor" de acuerdo a cómo se da la comunicación entre ellos:
- Consumer-driven: cuando el servicio que consumirá un servicio define qué espera (ejemplo: “cuando envío una transacción, quiero recibir un status=APPROVED”).
- Provider-driven: el proveedor define qué ofrece y los consumidores validan contra esa definición.
Discutir las ventajas y desventajas de cada tipo de contrato está fuera del alcance de este blog, así que en la siguiente tabla vemos un resumen de sus características:
Aspecto | Consumer-Driven | Provider-Driven |
---|---|---|
Quién define el contrato | El consumidor establece sus expectativas sobre el proveedor. | El proveedor publica la especificación oficial de la API. |
Orientación | Centrado en las necesidades de negocio de los consumidores. | Centrado en la estandarización y gobernanza del proveedor. |
Ecosistema típico | Microservicios internos en organizaciones grandes (ej. orquestación de pagos, clearing, conciliaciones). | APIs públicas o interbancarias, donde los consumidores pueden ser externos y desconocidos. |
Agilidad | Alta: los consumidores empujan cambios según sus necesidades. | Menor: depende de la evolución del proveedor; consumidores deben esperar nuevas versiones. |
Riesgo principal | Fragmentación de contratos si hay demasiados consumidores con expectativas distintas. | APIs poco útiles o sobredimensionadas si el proveedor no considera el contexto real de uso. |
Cumplimiento normativo | Requiere coordinar trazabilidad entre múltiples contratos (más complejo en auditorías). | Más fácil de auditar: existe una “fuente única de verdad” centralizada. |
Ejemplo financiero | Un servicio de conciliación define qué datos necesita de un servicio de pagos, validando que siempre reciba transactionId , amount y status . |
Una API bancaria regulatoria (ej. open banking) publica un contrato OpenAPI estandarizado; todos los fintech deben implementarlo igual. |
Ventaja clave | Los cambios son validados antes de llegar al proveedor > menos riesgo de romper consumidores críticos. | Uniformidad y gobernanza centralizada → útil en entornos regulados y APIs de acceso masivo. |
Desventaja clave | Puede generar mucha sobrecarga al proveedor si hay que atender contratos de múltiples consumidores. | Puede dejar a los consumidores sin voz, con APIs que no resuelven del todo sus necesidades. |
En el caso de nuestro cliente, el esquema que eligieron fue el de provider-driven, ya que su objetivo principal era tener a mano una única fuente de verdad centralizada sobre todas las APIs para empezar a poner orden al desarrollo de sus APIs.
Qué son las pruebas de contratos
Una prueba de contrato valida que los servicios definidos en este cumplen con las expectativas de los consumidores o proveedores que dependen de él. En lugar de hacer verificar la integración entre los servicios "en vivo", se trabaja con contratos preacordados, que funcionan como un acuerdo legal entre servicios.
Ejemplo de prueba de contrato en acción
Tomando como ejemplo nuestros servicios de pagos y autorización asumiremos que el equipo que está a cargo del servicio de autorización decide hacer un cambio a la respuesta renombrando el key "status" a "response" del JSON quedando de esta manera:
{
"request": {
"method": "POST",
"path": "/transactions",
"body": { "amount": 100, "currency": "PYG" }
},
"response": {
"status": 201,
"body": { "transactionId": "abc123", "response": "APPROVED"}
}
}
Este cambio es aprobado internamente y empieza el siguiente flujo:
- Se actualiza el repositorio que contiene el contrato, normalmente este sería un archivo OpenAPI en un repositorio accesible por todos los consumidores.
- Este cambio dispara un pipeline CI/CD (a cargo del proveedor) que hará validaciones estáticas como sintaxis y reglas internas de naming, versionado y consistencia de errores.
- Si las validaciones estáticas son correctas, se lo desplegará en un ambiente de integración, que consiste en crear un mock-server y validar las respuestas del mismo.
- En este punto la prueba falla, porque ya no se recibe el key "status" esperado, esto es notado por el equipo responsable y el cambio es revisado.
- Si el problema no es detectado aquí debido a una prueba incompleta, se actualizará la rama principal del repositorio de contratos, aquí empieza la tarea de los consumidores.
- En el lado del servicio consumidor de "pagos" se disparará un pipeline CI/CD que creará un mock-server del mismo y ejecutará tests unitarios contra el mismo.
- Al no recibir el key "status", el pipeline fallará y esto será notado por el equipo de "pagos", quienes podrán avisar al equipo de "autorización" o directamente evitar usar la última versión de esta API.
Como podemos ver, tener un esquema de validaciones de contratos robusto nos da un arma muy poderosa para mejorar la confiabilidad del proceso de desarrollo, porque permite que los problemas de integración sean detectados incluso antes de escribir una sola línea de código, los cuales normalmente se suelen detectar luego de un pase a producción creando una gran cantidad de problemas.
Por qué son críticas en fintech
En un sistema financiero, cada microservicio tiene un rol delicado. Un cambio en el servicio de autenticación puede impedir que el de autorización de transacciones funcione, lo cual impacta en el de reportes regulatorios y así sucesivamente.
Las consecuencias son serias:
- Downtime en pagos -> pérdida de confianza del cliente.
- Inconsistencias en conciliación -> riesgos regulatorios.
- Errores en auditorías -> multas o sanciones.
Las pruebas de contrato ayudan a reducir estos riesgos porque:
- Se ejecutan rápido, sin necesidad de montar todo un entorno.
- Detectan incompatibilidades en etapas tempranas del ciclo de vida.
- Aseguran backward compatibility: que un nuevo release no rompa a consumidores existentes.
En términos simples: nos dan garantías técnicas comparables a las garantías legales que exige el regulador.
Aparte de estas ventajas a la hora de reducir riesgos, otras ventajas que notamos luego de que nuestro cliente adopte estas pruebas fueron:
- Documentación más sólida: tener un panorama de todos los servicios que proveía el cliente era muy difícil, ahora esto se encuentra fácilmente en un repositorio dedicado de contratos.
- Testing independiente: cada equipo puede ejecutar la mayoría de los tests de integración sin depender de un ambiente dedicado de pruebas.
- Desarrollo independiente: cada equipo puede tener a mano las especificaciones de todas las APIs de la organización junto con la capacidad de mockearlas, lo cual facilita mucho el desarrollo de nuevas funcionalidades.
Conclusión
En fintech, donde los errores de integración tienen impacto directo en clientes, reguladores y reputación, las pruebas de contrato no son un “nice to have”: son una necesidad.
Adoptarlas permite:
- Detectar fallas temprano.
- Reducir el costo de pruebas de integración.
- Garantizar que la evolución de un servicio no rompe a otro.
- Mejorar la documentación de gobernanza de la organización.
En conclusión, los contratos son para los microservicios lo que los acuerdos regulatorios son para la empresa: garantías de que todos cumplen lo prometido.