HTTP server configuration¶
This guide covers how to configure the HTTP Server handler for mocking external services in integration tests.
Overview¶
The HTTP Server handler creates a local mock server that can stub HTTP endpoints. This is useful for testing how your application interacts with external APIs without actually calling them.
Use cases¶
- Mock third-party APIs (payment gateways, notification services, etc.)
- Simulate various response scenarios (success, errors, timeouts)
- Verify your application sends correct requests
- Test retry logic and error handling
Resource configuration¶
The HTTP Server is a standalone handler that doesn't require a container:
Resource options¶
| Option | Type | Default | Description |
|---|---|---|---|
port |
int | 0 (random) |
Port to listen on. Use 0 for system-assigned port, or specify a fixed port |
Configuring your app to use mock servers¶
Since your application needs to call the mock server instead of the real external API, you must configure the mock server URLs via environment variables.
Use the {{.resource_name.url}} template syntax to reference mock server URLs:
resources:
payment-api:
type: http-server
options:
port: 9001
notification-api:
type: http-server
options:
port: 9002
app:
command: go run ./cmd/server
env:
# Use template syntax to reference mock server URLs
PAYMENT_API_URL: "{{.payment-api.url}}"
NOTIFICATION_API_URL: "{{.notification-api.url}}"
The templates resolve to:
- Command mode: http://localhost:{port} (e.g., http://localhost:9001)
- Container mode: http://host.docker.internal:{port} (accessible from Docker)
Your application code should read these URLs from environment variables:
paymentAPIURL := os.Getenv("PAYMENT_API_URL")
// Use paymentAPIURL when making HTTP calls to payment service
Fixed Ports Required
HTTP server resources must have a fixed port configured in options for the URL template to work. Without a port, the template cannot be resolved.
Complete example¶
Here's a complete tomato.yml with multiple mock servers:
version: 2
settings:
timeout: 5m
fail_fast: false
output: pretty
resources:
# Mock external payment gateway
payment-api:
type: http-server
options:
port: 9001
# Mock notification service
notification-api:
type: http-server
options:
port: 9002
# HTTP client to test your app
api:
type: http-client
base_url: "http://localhost:8080"
app:
command: go run ./cmd/server
port: 8080
env:
# Templates resolve to http://localhost:9001, http://localhost:9002
PAYMENT_API_URL: "{{.payment-api.url}}"
NOTIFICATION_API_URL: "{{.notification-api.url}}"
ready:
type: http
path: /health
timeout: 30s
features:
paths:
- ./features
Writing HTTP server tests¶
Basic stubbing¶
Feature: Payment Processing
Scenario: Process successful payment
# Stub the payment gateway
Given "payment-api" stub "POST" "/charge" returns "200" with json:
"""
{"transaction_id": "txn_123", "status": "success"}
"""
# Your app calls the payment gateway
When "api" sends "POST" to "/process-payment" with json:
"""
{"amount": 100, "currency": "USD"}
"""
Then "api" response status is "200"
And "api" response json "status" is "completed"
Stubbing different responses¶
Scenario: Handle payment gateway error
Given "payment-api" stub "POST" "/charge" returns "500" with json:
"""
{"error": "Gateway unavailable"}
"""
When "api" sends "POST" to "/process-payment" with json:
"""
{"amount": 100}
"""
Then "api" response status is "503"
Custom response headers¶
Scenario: API returns rate limit headers
Given "payment-api" stub "GET" "/balance" returns "200" with headers:
| header | value |
| X-RateLimit-Remaining | 99 |
| X-RateLimit-Reset | 3600 |
When "api" sends "GET" to "/check-balance"
Then "api" response status is "200"
Verifying requests¶
Scenario: Verify authorization header is sent
Given "payment-api" stub "POST" "/charge" returns "200"
When "api" sends "POST" to "/process-payment" with json:
"""
{"amount": 50}
"""
Then "payment-api" received "POST" "/charge"
And "payment-api" received request with header "Authorization" containing "Bearer"
Scenario: Verify request body
Given "payment-api" stub "POST" "/charge" returns "200"
When "api" sends "POST" to "/process-payment" with json:
"""
{"amount": 75, "currency": "EUR"}
"""
Then "payment-api" received request with body containing "EUR"
Request counting¶
Scenario: Verify retry behavior
# Stub fails first two times, succeeds third
Given "payment-api" stub "POST" "/charge" returns "503"
When "api" sends "POST" to "/process-payment-with-retry"
# App should have retried 3 times
Then "payment-api" received "POST" "/charge" "3" times
Negative assertions¶
Scenario: Verify no unauthorized calls
Given "payment-api" stub "GET" "/balance" returns "200"
When "api" sends "GET" to "/public-info"
Then "payment-api" did not receive "GET" "/balance"
See HTTP Server Steps for the complete list of available steps.
Multiple mock servers¶
You can configure multiple mock servers for different external services:
resources:
payment-api:
type: http-server
options:
port: 9001
notification-api:
type: http-server
options:
port: 9002
analytics-api:
type: http-server
options:
port: 9003
Reset behavior¶
Between each scenario: - All stubs are cleared - All recorded calls are cleared
This ensures each scenario starts with a clean slate.
Troubleshooting¶
Port already in use¶
If you get "address already in use" error:
- Use
port: 0to let the system assign a free port - Or kill processes using the specified port:
lsof -ti:9999 | xargs kill -9
Requests not being recorded¶
- Verify the URL in your test matches the stub path exactly
- Check that the HTTP method matches (GET vs POST)
- Ensure your application is pointing to the mock server's port
Stub not matching¶
Stubs match in order they were defined. If a request doesn't match any stub, the server returns 404 with a message indicating no stub was found.