Cualquier organización que ha migrado a microservicios acaba topándose con el mismo problema: los tests end-to-end se vuelven impracticables. Cuando tienes 30 servicios, montar todos juntos en un entorno de staging para ejecutar un test e2e es lento, frágil y caro. Testing por contrato — y su implementación más popular, Pact — resuelve el problema moviéndolo a otra capa.
El problema con los tests E2E a escala
Los tests E2E asumen que puedes desplegar un entorno completo y ejercitar un flujo de usuario de punta a punta. En un monolito o 3-4 servicios funciona. Con 30+ servicios emergen tres patologías:
- Tiempo de setup prohibitivo. Levantar todo el stack para cada test lleva minutos o decenas de minutos. El CI se convierte en la queja recurrente del equipo.
- Fragilidad compuesta. Si cada servicio tiene 1% de flakiness, combinarlos multiplica la probabilidad de fallo falso. Los tests fallan sin culpables claros.
- Feedback tardío. Un cambio rompe la integración de dos servicios — lo descubres horas después en staging, no en la PR donde ocurrió.
La respuesta colectiva de la industria desde 2015: testing por contrato.
Qué es Pact
Pact implementa consumer-driven contract testing. La idea en tres pasos:
- El consumidor define el contrato. El servicio que llama (p.ej., el front) describe qué respuesta espera de su proveedor (p.ej., la API de pedidos) para cada caso relevante.
- Pact genera un fichero contrato (JSON). Se publica a un broker central (Pact Broker).
- El proveedor verifica contra el contrato. En su propio pipeline, el servicio proveedor ejecuta los contratos publicados por sus consumidores y comprueba que cumple cada expectativa.
Resultado: consumidor y proveedor se testean de forma desacoplada. Cada uno lo hace en su propio CI, sin necesidad de entorno compartido. La prueba de integración existe, pero en forma de contrato revisado por ambos.
Un ejemplo concreto
Supongamos un front (consumidor) que llama al servicio orders-api (proveedor) para obtener un pedido por ID.
En el lado del consumidor, el test escribe el contrato esperado:
// Pact JS (consumidor)
await provider.addInteraction({
state: "order 42 exists",
uponReceiving: "a request for order 42",
withRequest: {
method: "GET",
path: "/orders/42",
},
willRespondWith: {
status: 200,
body: {
id: 42,
total: 99.99,
items: Matchers.eachLike({ sku: "ABC", qty: 1 })
}
}
});
Se ejecuta el test con un mock Pact — pasa o no pasa en el lado consumidor, generando el fichero contrato.
En el lado del proveedor:
# Pact Ruby (proveedor)
Pact.service_provider "orders-api" do
honours_pact_with "frontend" do
pact_uri "http://pact-broker/pacts/provider/orders-api/consumer/frontend/latest"
end
provider_state "order 42 exists" do
set_up do
Order.create!(id: 42, total: 99.99, items: [...])
end
end
end
El pipeline del proveedor descarga el contrato, lo reproduce contra una instancia del servicio, y verifica que todas las expectativas del consumidor se cumplen.
Lo que hace bien y lo que no
Pact brilla en:
- Verificar cambios no compatibles antes de desplegar. Si
orders-apirenombra un campo, el test de verificación falla antes de llegar a producción. - Reducir tiempos de CI. Cada servicio se testea aislado en segundos, no en minutos con docker-compose gigantes.
- Documentación ejecutable. El contrato es en sí documentación de la API que no se desactualiza porque falla si diverges.
Lo que no resuelve:
- Lógica de negocio emergente. Si el bug está en la composición de 5 servicios (e.g., un flow de checkout), el contrato entre dos no lo detecta.
- Performance y concurrencia. Contract tests son unitarios; load testing sigue necesitando su propio enfoque.
- Schemas internos del proveedor. Solo valida la interfaz con los consumidores; la estructura interna puede cambiar sin fallar el contrato.
Flujo de trabajo con Pact Broker
En un entorno con varios equipos, Pact Broker hace de hub:
- El CI del consumidor publica el contrato después de sus tests.
- El Broker versiona contratos por commit/tag y notifica al proveedor.
- El CI del proveedor verifica y reporta al Broker si pasa o no.
- El Broker mantiene una matriz de compatibilidad: “¿qué versión del proveedor funciona con qué versión del consumidor?” — útil para gates de despliegue.
Soluciones alternativas: Pactflow (broker gestionado), o rodar tu propio broker con pact-broker Docker image.
Qué pide Pact del proceso
Pact funciona bien cuando se dan tres condiciones:
- Cultura de ownership. Consumidores saben qué quieren, proveedores aceptan cumplirlo. Si no hay esta cultura, los contratos terminan siendo un “pelea por el contrato” entre equipos.
- Pipeline CI maduro. Cada servicio necesita su propio pipeline que publique y verifique contratos automáticamente. Sin esto, el valor se pierde.
- Acuerdo sobre “compatible”. A veces el proveedor cambia algo que rompe solo a un consumidor concreto. Pact fuerza la conversación, pero el equipo debe definir el criterio.
Ver también cómo metodologías ágiles ayudan a coordinar equipos cuando la coordinación emerge de contratos y no de reuniones.
Conclusión
Contract testing con Pact es la respuesta probada al problema de tests E2E en arquitecturas distribuidas. No reemplaza todas las formas de testing, pero sí elimina la mayor parte de los tests E2E — los que se estaban rompiendo sin valor añadido. Para cualquier equipo con 5+ microservicios, introducir Pact paga su inversión en el primer trimestre.
Síguenos en jacar.es para más sobre testing, microservicios y pipelines CI modernos.