Microservices Example
Learn how to test microservices architectures with service-to-service communication, API gateway routing, and header propagation.
Overview
This example simulates a microservices setup with:
- API Gateway: Routes requests to backend services
- User Service: Mock service for user management
- Order Service: Mock service for order processing
- Payment Service: Mock service for payment processing
Use Case
Perfect for:
- Microservices architecture testing
- API gateway validation
- Service mesh testing
- Service-to-service communication validation
- Load balancer testing
- Integration testing across multiple services
Architecture
Client → Nginx (API Gateway) → Backend Services (Mocked)
├── user-service
├── order-service
└── payment-serviceAPI Routes
| Path | Upstream Service | Description |
|---|---|---|
/users/* | user-service | User management operations |
/orders/* | order-service | Order processing |
/payments/* | payment-service | Payment handling |
/health | gateway | Gateway health check |
File Structure
microservices/
├── Dockerfile
├── nginx.conf
└── .httptests/
├── config.yml
└── test.jsonConfiguration Files
Dockerfile
dockerfile
FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]nginx.conf
nginx
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name _;
# Health check
location = /health {
default_type application/json;
return 200 '{"status": "healthy", "gateway": "api-gateway", "version": "1.0"}';
}
# User service
location /users/ {
proxy_pass http://user-service:80/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Service-Name "user-service";
proxy_set_header X-Gateway "api-gateway";
proxy_pass_request_headers on;
}
# Order service
location /orders/ {
proxy_pass http://order-service:80/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Service-Name "order-service";
proxy_set_header X-Gateway "api-gateway";
proxy_pass_request_headers on;
}
# Payment service
location /payments/ {
proxy_pass http://payment-service:80/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Service-Name "payment-service";
proxy_set_header X-Gateway "api-gateway";
proxy_pass_request_headers on;
}
}
}Key features:
- Routes requests based on path prefixes
- Adds service identification headers
- Forwards standard proxy headers
- Passes through all client headers
.httptests/config.yml
yaml
mock:
network_aliases:
- user-service
- order-service
- payment-service
nginx:
environment:
GATEWAY_VERSION: v1
ENVIRONMENT: testConfiguration breakdown:
- Three network aliases simulate three different microservices
- All aliases point to the same mock service (simplifies testing)
- Environment variables for the gateway
.httptests/test.json
json
{
"collectionHeaders": [
["X-Request-ID"],
["X-Client-Version"]
],
"hosts": {
"gateway.microservices.local": [
{
"paths": ["/health"],
"method": "GET",
"expectedStatus": 200,
"expectedResponseHeaders": [
["Content-Type", "application/json"]
]
},
{
"paths": ["/users/profile"],
"method": "GET",
"expectedStatus": 200,
"expectedRequestHeadersToUpstream": [
["$collectionHeaders"],
["X-Forwarded-For"],
["X-Real-IP"],
["X-Service-Name", "user-service"],
["X-Gateway", "api-gateway"]
]
},
{
"paths": ["/orders/12345"],
"method": "GET",
"expectedStatus": 200,
"expectedRequestHeadersToUpstream": [
["$collectionHeaders"],
["X-Forwarded-For"],
["X-Real-IP"],
["X-Service-Name", "order-service"],
["X-Gateway", "api-gateway"]
]
},
{
"paths": ["/payments/status"],
"method": "GET",
"expectedStatus": 200,
"expectedRequestHeadersToUpstream": [
["$collectionHeaders"],
["X-Forwarded-For"],
["X-Real-IP"],
["X-Service-Name", "payment-service"],
["X-Gateway", "api-gateway"]
]
}
]
}
}Test Coverage
The test suite validates:
- ✅ Gateway routing to correct upstream services
- ✅ Request header forwarding between services
- ✅ Service-specific identification headers
- ✅ Collection header propagation
- ✅ Health check endpoints
- ✅ Cross-service communication patterns
GitHub Actions Workflow
yaml
name: Microservices Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: serviceguards-com/httptests-action@v2
with:
httptests-directory: ./microservicesExpected Test Output
🔧 Adding X-Upstream-Target headers to nginx configs...
🚀 Starting environment...
🧪 Running tests for httptests-microservices
test_endpoints (__main__.IntegrationTests.test_endpoints) ... ok
→ Testing: GET gateway.microservices.local/health
✓ Status code: 200 (expected 200)
✓ Response header: content-type = application/json
→ Testing: GET gateway.microservices.local/users/profile
✓ Status code: 200 (expected 200)
✓ Request header forwarded: x-request-id
✓ Request header forwarded: x-client-version
✓ Request header forwarded: x-forwarded-for
✓ Request header forwarded: x-real-ip
✓ Request header: x-service-name = user-service
✓ Request header: x-gateway = api-gateway
→ Testing: GET gateway.microservices.local/orders/12345
✓ Status code: 200 (expected 200)
✓ Request header: x-service-name = order-service
✓ Request header: x-gateway = api-gateway
→ Testing: GET gateway.microservices.local/payments/status
✓ Status code: 200 (expected 200)
✓ Request header: x-service-name = payment-service
✓ Request header: x-gateway = api-gateway
============================================================
Total assertions passed: 18
============================================================Key Testing Patterns
Service Identification
Each route adds a service identifier header:
nginx
location /users/ {
proxy_pass http://user-service:80/;
proxy_set_header X-Service-Name "user-service";
}Tests verify this header reaches the upstream:
json
{
"paths": ["/users/profile"],
"expectedRequestHeadersToUpstream": [
["X-Service-Name", "user-service"]
]
}Gateway Metadata
The gateway adds its own identification:
nginx
proxy_set_header X-Gateway "api-gateway";This helps services know they're being called through the gateway.
Collection Headers
Client-side headers are defined once and reused:
json
"collectionHeaders": [
["X-Request-ID"],
["X-Client-Version"]
]Referenced in tests:
json
"expectedRequestHeadersToUpstream": [
["$collectionHeaders"] // Expands to both headers
]Extending This Example
Add Service Discovery
Simulate dynamic service discovery with Consul or etcd:
nginx
# Use variables for service endpoints
set $user_service_url "http://user-service:80";
location /users/ {
proxy_pass $user_service_url/;
}Add Load Balancing
yaml
# config.yml
mock:
additional_ports:
- 8081
- 8082
- 8083
network_aliases:
- user-service-1
- user-service-2
- user-service-3nginx
upstream user_service_cluster {
server user-service-1:8081;
server user-service-2:8082;
server user-service-3:8083;
}
location /users/ {
proxy_pass http://user_service_cluster/;
}Add Circuit Breaking
Simulate timeout and retry behavior:
nginx
location /users/ {
proxy_pass http://user-service:80/;
proxy_connect_timeout 5s;
proxy_send_timeout 10s;
proxy_read_timeout 10s;
proxy_next_upstream error timeout;
}Add Service Authentication
Services authenticate with each other:
nginx
location /users/ {
proxy_pass http://user-service:80/;
proxy_set_header X-Service-Token "gateway-service-token-12345";
proxy_set_header X-Calling-Service "api-gateway";
}Test it:
json
{
"paths": ["/users/profile"],
"expectedRequestHeadersToUpstream": [
["X-Service-Token", "gateway-service-token-12345"],
["X-Calling-Service", "api-gateway"]
]
}Add Correlation IDs
Track requests across services:
nginx
# Generate or forward correlation ID
location /users/ {
proxy_pass http://user-service:80/;
proxy_set_header X-Correlation-ID $http_x_correlation_id;
}Real-World Microservices Patterns
Service Mesh Headers
json
{
"paths": ["/users/profile"],
"expectedRequestHeadersToUpstream": [
["X-Service-Name", "user-service"],
["X-Service-Version", "v1"],
["X-Request-ID"],
["X-Trace-ID"],
["X-Span-ID"]
]
}Health Check Aggregation
nginx
location /health {
# Aggregate health from all services
default_type application/json;
return 200 '{"status": "healthy", "services": ["user", "order", "payment"]}';
}API Versioning
nginx
location /v1/users/ {
proxy_pass http://user-service:80/v1/;
proxy_set_header X-API-Version "v1";
}
location /v2/users/ {
proxy_pass http://user-service-v2:80/v2/;
proxy_set_header X-API-Version "v2";
}Related Examples
- API Gateway - Authentication and rate limiting
- Nginx Proxy - Basic proxy configuration
- API Testing - REST API testing
Next Steps
- Explore API Gateway example for advanced features
- Learn about Matrix Testing for parallel execution
- Understand Upstream Tracking in depth