El Desafío del Testing de Microservicios
Las arquitecturas de microservicios dividen una aplicación monolítica en docenas o cientos de servicios desplegables independientemente. Cada servicio maneja sus propios datos, se comunica a través de la red y puede estar escrito en un lenguaje diferente. Esto trae flexibilidad de deployment pero aumenta dramáticamente la complejidad del testing.
En un monolito, pruebas una aplicación. En microservicios, pruebas muchos servicios y las interacciones entre ellos. Un bug puede no existir en ningún servicio individual — puede aparecer solo cuando el Servicio A envía un mensaje específico al Servicio B, que dispara el Servicio C.
La Pirámide de Testing para Microservicios
La pirámide de testing tradicional (unit > integration > E2E) sigue aplicando, pero necesita adaptación para sistemas distribuidos.
Capa 1: Unit Tests (Base)
Prueban funciones y clases individuales dentro de un solo servicio. Son rápidos, numerosos y se ejecutan sin dependencias externas.
Servicio A
├── Unit tests para lógica de negocio
├── Unit tests para transformación de datos
└── Unit tests para reglas de validación
Objetivo: Verificar que la lógica interna de cada servicio es correcta.
Capa 2: Component Tests
Esta capa es única de microservicios. Un component test verifica un solo servicio como un todo, con sus dependencias externas (bases de datos, otros servicios) reemplazadas por test doubles.
┌─────────────────────────┐
│ Component Test │
│ ┌───────────────────┐ │
│ │ Servicio A │ │
│ │ (código real) │ │
│ └───────────────────┘ │
│ ┌─────────┐ ┌────────┐ │
│ │ Mock DB │ │Mock Svc│ │
│ │ │ │ B │ │
│ └─────────┘ └────────┘ │
└─────────────────────────┘
Los component tests inician el servicio, envían solicitudes HTTP a sus endpoints reales y verifican las respuestas. La diferencia con los integration tests es que todas las llamadas externas están mockeadas.
Ejemplo con base de datos en memoria:
describe('User Service Component', () => {
let app;
beforeAll(async () => {
app = await startService({
database: 'sqlite::memory:',
paymentService: mockPaymentService,
emailService: mockEmailService,
});
});
test('POST /users crea un usuario y retorna 201', async () => {
const response = await request(app)
.post('/users')
.send({ name: 'Alice', email: 'alice@example.com' });
expect(response.status).toBe(201);
expect(response.body.name).toBe('Alice');
});
test('POST /users con email duplicado retorna 409', async () => {
await request(app)
.post('/users')
.send({ name: 'Alice', email: 'alice@example.com' });
const response = await request(app)
.post('/users')
.send({ name: 'Bob', email: 'alice@example.com' });
expect(response.status).toBe(409);
});
});
Capa 3: Integration Tests
Verifican que dos o más servicios se comunican correctamente. Estos tests usan dependencias reales (o containerizadas).
Integración servicio-a-servicio:
- Servicio A llama a la API del Servicio B — ¿el formato de la solicitud coincide?
- Servicio A publica un evento — ¿el Servicio B lo consume correctamente?
Integración servicio-a-infraestructura:
- Servicio A se conecta a PostgreSQL — ¿funcionan las queries?
- Servicio A publica en Kafka — ¿se entregan los mensajes?
- Servicio A lee del cache Redis — ¿se respetan los TTLs?
Capa 4: Contract Tests
Los contract tests verifican que la interfaz entre dos servicios permanezca compatible. Un consumidor define qué espera; el proveedor verifica que puede cumplir esas expectativas.
Esta capa es crítica en microservicios porque los servicios se despliegan independientemente. Sin contract tests, el Servicio Proveedor podría desplegar un cambio que rompa al Servicio Consumidor sin descubrirlo hasta producción.
Capa 5: End-to-End Tests (Tope)
Los tests E2E verifican flujos de negocio completos a través de múltiples servicios. Son los más realistas pero también los más frágiles, lentos y costosos.
Minimiza los tests E2E. Solo cubre rutas de negocio críticas: registro de usuario, procesamiento de pagos, flujos core.
El Testing Honeycomb
Algunos equipos prefieren el testing honeycomb sobre la pirámide para microservicios. En este modelo, los integration y component tests son la capa más ancha — no los unit tests. La razón: en microservicios, la mayoría de los bugs ocurren en las fronteras de los servicios, no dentro de la lógica de negocio.
Estrategias de Testing por Patrón de Comunicación
Sincrónico (REST/gRPC)
Cuando el Servicio A hace una llamada HTTP o gRPC al Servicio B:
- Component test: Mockea la respuesta del Servicio B en los tests del Servicio A.
- Contract test: Verifica que los schemas request/response coincidan.
- Integration test: Levanta ambos servicios (Docker Compose) y verifica la llamada real.
Asincrónico (Eventos/Mensajes)
Cuando el Servicio A publica un evento que consume el Servicio B:
- Component test: Verifica que el Servicio A publica la forma correcta del evento. Verifica que el Servicio B procesa un evento dado correctamente.
- Contract test: Define el schema del evento como un contrato.
- Integration test: Publica un evento real y verifica que el Servicio B lo procesa.
Coreografía vs Orquestación
En coreografía, los servicios reaccionan a eventos independientemente — el testing requiere verificar la cadena de eventos. En orquestación, un coordinador central (patrón Saga) maneja el flujo — probar el orquestador se convierte en prioridad.
Estrategia de Ambientes
Desarrollo Local
Usa Docker Compose para ejecutar servicios dependientes localmente:
services:
user-service:
build: ./user-service
ports: ["3001:3001"]
depends_on: [postgres, redis]
order-service:
build: ./order-service
ports: ["3002:3002"]
depends_on: [postgres, kafka]
postgres:
image: postgres:16
redis:
image: redis:7
kafka:
image: confluentinc/cp-kafka:7.5.0
Pipeline de CI
Ejecuta component e integration tests en CI usando contenedores Docker. Mantén los tests E2E en un stage separado que se ejecute con menor frecuencia (nightly o en ramas de release).
Ejercicio: Diseña una Estrategia de Testing de Microservicios
Eres el QA Lead de una plataforma de e-commerce con los siguientes microservicios:
Arquitectura del Sistema
┌──────────┐ ┌──────────┐ ┌──────────┐
│ API │────▶│ User │────▶│ Auth │
│ Gateway │ │ Service │ │ Service │
└──────────┘ └──────────┘ └──────────┘
│
▼
┌──────────┐ ┌──────────┐ ┌───────────────┐
│ Order │────▶│ Payment │────▶│ Notification │
│ Service │ │ Service │ │ Service │
└──────────┘ └──────────┘ └───────────────┘
│
▼
┌──────────┐ ┌──────────┐
│ Inventory│────▶│ Shipping │
│ Service │ │ Service │
└──────────┘ └──────────┘
Patrones de comunicación:
- API Gateway → Servicios: REST (sincrónico)
- Order Service → Payment Service: REST (sincrónico)
- Order Service → Inventory Service: eventos Kafka (asincrónico)
- Payment Service → Notification Service: eventos Kafka (asincrónico)
Tarea 1: Distribución de Capas de Tests
Para cada capa de testing, lista los tests específicos que escribirías:
Unit tests (por servicio):
| Servicio | Qué Probar con Unit Tests |
|---|---|
| Order Service | Cálculo de total, lógica de descuentos, máquina de estados del pedido |
| Payment Service | Validación de pago, cálculo de reembolso, conversión de moneda |
| Inventory Service | Lógica de verificación de stock, expiración de reservas |
| Notification Service | Renderizado de templates, selección de canal (email/SMS/push) |
Component tests:
Para cada servicio, define 3-5 component tests.
Contract tests:
Define pares consumidor-proveedor:
| Consumidor | Proveedor | Contrato |
|---|---|---|
| Order Service | Payment Service | POST /payments: schema request/response |
| Order Service | Inventory Service | Schema evento ReserveStock |
| Payment Service | Notification Service | Schema evento PaymentConfirmed |
E2E tests (solo rutas críticas):
- Usuario se registra → inicia sesión → crea pedido → pago exitoso → inventario actualizado → envío iniciado → notificación enviada.
- Usuario crea pedido → pago falla → pedido cancelado → inventario liberado.
- Usuario crea pedido → solicita reembolso → reembolso procesado → notificación enviada.
Tarea 2: Escenarios de Falla
Para cada servicio, define qué sucede cuando falla y cómo lo probarías:
| Servicio Caído | Impacto | Estrategia de Test |
|---|---|---|
| Payment Service | Pedidos no pueden completarse | Verificar que Order Service encola reintentos |
| Inventory Service | Verificación de stock falla | Verificar que Order Service rechaza o encola pedidos |
| Notification Service | Usuarios no notificados | Verificar que pedidos completan (notificación no es crítica) |
Tarea 3: Diseño del Pipeline de CI
Diseña un pipeline de CI con estos stages:
Stage 1: Build & Unit Tests (por servicio, paralelo) — 2 min
Stage 2: Component Tests (por servicio, paralelo) — 5 min
Stage 3: Contract Tests (consumidor + proveedor, paralelo) — 3 min
Stage 4: Integration Tests (Docker Compose) — 10 min
Stage 5: E2E Smoke Tests (staging) — 15 min (solo nightly)
Entregables
- Un documento de estrategia de testing listando conteo de tests por capa por servicio.
- Un diagrama del pipeline de CI mostrando stages de tests y gates.
- Una matriz de riesgos identificando qué fallas de servicio son mayor prioridad para probar.