Hoverfly: Capture and Simulate API Traffic for Testing

Hoverfly: Capture and Simulate API Traffic for Testing

One of the hardest parts of building integration tests is creating realistic test data. You can write mock responses by hand, but they drift from reality. You can call the real API in tests, but that introduces network dependency and flakiness.

Hoverfly solves this with a record-and-replay approach. It runs as a proxy between your application and real APIs, records the actual traffic, and then replays that recorded traffic in simulations. Your tests run against traffic captured from reality.

What Is Hoverfly?

Hoverfly is an open-source API simulation tool developed by SpectoLabs. It operates as an HTTP/HTTPS proxy with two primary modes:

  • Capture mode: Hoverfly sits between your application and real APIs, recording all request/response pairs as "simulations"
  • Simulate mode: Hoverfly replays recorded simulations without making real network calls

Beyond basic record/replay, Hoverfly supports:

  • Spy mode: Simulate known traffic, pass through unknown requests to real services
  • Synthesize mode: Generate responses dynamically with middleware
  • Modify mode: Intercept and alter real traffic in flight

Installation

Binary (macOS):

brew install hoverfly/tap/hoverfly

Binary (Linux/Windows): Download from hoverfly.io. Two binaries are included:

  • hoverfly — the proxy server
  • hoverctl — CLI control tool

Docker:

docker pull spectolabs/hoverfly

Core Concepts

Simulations

A simulation is a JSON file containing captured request/response pairs. Each entry includes:

  • Request matcher (URL, method, headers, body)
  • Response (status, headers, body)

Simulations can be exported, committed to version control, and shared across teams.

Admin API

Hoverfly exposes an admin API on port 8888. hoverctl is a CLI wrapper for this API. You can also call it directly for automation:

curl http://localhost:8888/api/v2/hoverfly/mode

Starting Hoverfly

# Start Hoverfly (proxy on 8500, admin on 8888)
hoverfly

<span class="hljs-comment"># Or with custom ports
hoverfly -pp 9000 -ap 9090

Start the control tool:

hoverctl start

Check status:

hoverctl status

Capturing API Traffic

Setting capture mode

hoverctl mode capture

Routing traffic through Hoverfly

Configure your application or HTTP client to use Hoverfly as a proxy:

curl:

curl --proxy http://localhost:8500 https://api.example.com/users

Python requests:

import requests

proxies = {
    'http': 'http://localhost:8500',
    'https': 'http://localhost:8500',
}

response = requests.get('https://api.example.com/users', proxies=proxies, verify=False)

Node.js (axios):

const axios = require('axios')
const HttpsProxyAgent = require('https-proxy-agent')

const agent = new HttpsProxyAgent('http://localhost:8500')

const response = await axios.get('https://api.example.com/users', {
  httpsAgent: agent,
})

Go:

proxyURL, _ := url.Parse("http://localhost:8500")
transport := &http.Transport{
    Proxy: http.ProxyURL(proxyURL),
    TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: transport}
resp, err := client.Get("https://api.example.com/users")

HTTPS and certificate handling

For HTTPS traffic, Hoverfly performs TLS termination. Your client needs to trust Hoverfly's certificate:

# Export Hoverfly's certificate
hoverctl certificate --<span class="hljs-built_in">export cert.pem

<span class="hljs-comment"># Configure your client to trust it
<span class="hljs-built_in">export SSL_CERT_FILE=cert.pem
<span class="hljs-comment"># or
<span class="hljs-built_in">export NODE_EXTRA_CA_CERTS=cert.pem

Alternatively, disable certificate verification for test environments (never in production).

Exporting the simulation

After capturing traffic, export it:

hoverctl export simulation.json

The JSON file contains all recorded request/response pairs. Commit this to version control.

Replay in Simulate Mode

# Import a previously captured simulation
hoverctl import simulation.json

<span class="hljs-comment"># Switch to simulate mode
hoverctl mode simulate

<span class="hljs-comment"># Now requests are served from the simulation
curl --proxy http://localhost:8500 https://api.example.com/users

No network calls are made. Hoverfly matches incoming requests against the simulation and returns stored responses.

Simulation Matching

Hoverfly uses matchers to determine which simulation entry responds to a request. The default is exact matching — the URL, method, and body must match exactly.

Glob matching

For flexible URL matching:

{
  "request": {
    "path": [
      {
        "matcher": "glob",
        "value": "/users/*"
      }
    ],
    "method": [
      {
        "matcher": "exact",
        "value": "GET"
      }
    ]
  },
  "response": {
    "status": 200,
    "body": "{\"id\": 1, \"name\": \"Alice\"}"
  }
}

Regex matching

{
  "request": {
    "path": [
      {
        "matcher": "regex",
        "value": "/users/[0-9]+"
      }
    ]
  }
}

JSONPath matching

Match on specific fields in a JSON body:

{
  "request": {
    "body": [
      {
        "matcher": "jsonpath",
        "value": "$.email"
      }
    ]
  }
}

Middleware

Middleware transforms requests and responses at runtime. Write middleware as any executable that reads from stdin and writes to stdout.

Example: Add latency

#!/usr/bin/env python3
# middleware/add-latency.py
import sys
import json
import time

data = json.load(sys.stdin)

if data['response']['status'] == 200:
    time.sleep(0.3)  # Add 300ms latency

print(json.dumps(data))

Make it executable and configure Hoverfly to use it:

chmod +x middleware/add-latency.py
hoverctl middleware --binary python3 --script middleware/add-latency.py

Example: Inject errors

#!/usr/bin/env python3
import sys
import json
import random

data = json.load(sys.stdin)

# 10% of requests get a 503
if random.random() < 0.1:
    data['response']['status'] = 503
    data['response']['body'] = json.dumps({'error': 'Service unavailable'})

print(json.dumps(data))

This lets you test your application's resilience without modifying mock files or the real service.

Example: Dynamic timestamps

#!/usr/bin/env python3
import sys
import json
from datetime import datetime

data = json.load(sys.stdin)

body = data['response'].get('body', '')
try:
    parsed = json.loads(body)
    if isinstance(parsed, dict):
        parsed['timestamp'] = datetime.utcnow().isoformat()
        data['response']['body'] = json.dumps(parsed)
except (json.JSONDecodeError, TypeError):
    pass

print(json.dumps(data))

CI Integration

GitHub Actions

name: Integration Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Install Hoverfly
        run: |
          wget https://github.com/SpectoLabs/hoverfly/releases/download/v1.9.0/hoverfly_bundle_linux_amd64.zip
          unzip hoverfly_bundle_linux_amd64.zip
          chmod +x hoverfly hoverctl
          sudo mv hoverfly hoverctl /usr/local/bin/

      - name: Start Hoverfly in simulate mode
        run: |
          hoverfly &
          sleep 2
          hoverctl import simulations/api-simulation.json
          hoverctl mode simulate

      - name: Run tests
        run: npm test
        env:
          HTTP_PROXY: http://localhost:8500
          HTTPS_PROXY: http://localhost:8500
          NODE_TLS_REJECT_UNAUTHORIZED: 0

Docker Compose

version: '3.8'
services:
  hoverfly:
    image: spectolabs/hoverfly
    ports:
      - "8500:8500"
      - "8888:8888"
    volumes:
      - ./simulations:/simulations
    command: -webserver -import /simulations/api-simulation.json

  app:
    build: .
    depends_on:
      - hoverfly
    environment:
      - HTTP_PROXY=http://hoverfly:8500
      - HTTPS_PROXY=http://hoverfly:8500
    ports:
      - "3000:3000"

Spy Mode for Hybrid Testing

Spy mode lets you simulate responses for known requests while passing unknown requests through to the real service:

hoverctl mode spy

This is useful when:

  • You're building new API integrations and want to capture traffic for the new endpoints while simulating existing ones
  • Your simulation doesn't have complete coverage and you want a safety net

Diff Mode for Regression Testing

Diff mode compares responses from your real API against a simulation, flagging discrepancies:

hoverctl mode diff

Make requests with diff mode enabled, then check for differences:

hoverctl diff get

This helps detect when an external API has changed in ways that break your simulation assumptions. Run it periodically in CI to catch API drift before it causes test failures.

Managing Simulations with hoverctl

# List all imported simulations
hoverctl simulations

<span class="hljs-comment"># Delete specific simulation
hoverctl simulations --delete

<span class="hljs-comment"># View current mode
hoverctl mode

<span class="hljs-comment"># Check logs
hoverctl logs

When to Use Hoverfly

Best use cases:

  1. Capturing complex third-party API responses — When the response schema is complex (Stripe, Twilio, AWS services), capturing real responses is faster than hand-writing them.
  2. Regression testing external API behavior — Use diff mode to detect when external APIs change.
  3. Testing across languages — Hoverfly is language-agnostic. Any application that can use an HTTP proxy works with it, regardless of language or framework.
  4. Network simulation — Add latency, packet loss, and error injection through middleware without modifying application code.
  5. Compliance and audit environments — Simulations serve as documentation of what external APIs your system calls and what responses it expects.

Less suitable for:

  • Unit tests (too much overhead for per-unit isolation)
  • Browser-level mocking (MSW is better suited)
  • Tests that need fine-grained per-test response control (Mountebank or MSW handle this better)

Conclusion

Hoverfly's capture-then-simulate workflow solves a real problem: creating realistic test fixtures without maintaining them manually. By recording actual API traffic and replaying it, your tests run against responses that match what the real API returns.

The middleware system adds flexibility — you can inject errors, add latency, and transform responses at runtime without editing simulation files. This makes Hoverfly a strong choice for integration and contract testing, especially for teams working with complex third-party APIs.

Start by capturing a session against your real dependencies, commit the simulation JSON, and run it in CI. Your integration tests gain realistic data and network independence in a single step.

Read more