The problem with hardcoded test responses
Every team ends up with the same problem: integration tests that call a real API, or a fake one built from JSON fixtures that drift out of sync with the actual spec.
Real API calls make tests slow, flaky, and dependent on credentials and network access in CI. JSON fixtures fall behind the spec silently — your tests keep passing while the contract changes underneath them.
There's a better approach: generate the mock server directly from your OpenAPI spec. When the spec changes, the mock updates automatically. No manual fixture maintenance, no live API calls in CI.
Step-by-step: set up APIGhost for integration tests
Install APIGhost
APIGhost is a Python CLI tool. Install it into your project's virtualenv or globally:
pip install git+https://github.com/Coding-Dev-Tools/apighost.git
Verify the install:
apighost --version
Point it at your OpenAPI spec
Start a mock server from any OpenAPI 3.x spec file or URL:
# From a local file
apighost serve api/openapi.yaml
# From a URL
apighost serve https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.0/petstore.yaml
The server starts on port 8080 by default. Every endpoint defined in the spec is immediately available, returning realistic fake data generated from your schema property names.
Test error paths with scenario switching
Edge-case testing usually means either mocking at the unit level or using a staging environment that may not support injecting errors. APIGhost's scenario system handles this without touching your test code:
# Start in the built-in "error" scenario
apighost serve api/openapi.yaml --scenario error
# Create a custom scenario for rate limiting
apighost scenario create rate-limited -d "Simulate 429 responses"
apighost scenario edit rate-limited "GET /users" --status 429 --body '{"error":"rate_limit_exceeded"}'
# Run with the custom scenario
apighost serve api/openapi.yaml --scenario rate-limited
Record once, replay forever (VCR mode)
If you have a real API available during setup, record its responses once and replay them in every subsequent test run. Responses are identical on every replay — no flakiness from network variance or rate limits:
# Record a session against the real API
apighost record api/openapi.yaml --output my-api-cassette
# Replay the cassette in CI — no network access needed
apighost replay my-api-cassette -p 8080
Cassettes are stored in ~/.apighost/cassettes/ as JSON and can be committed to version control alongside your test suite.
Wire it into GitHub Actions
Start the mock server as a background service before your test step. A short sleep lets the server bind before requests arrive:
name: Integration tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install git+https://github.com/Coding-Dev-Tools/apighost.git
- name: Start mock API server
run: |
apighost serve api/openapi.yaml -p 8080 &
sleep 2
- name: Run integration tests
run: pytest tests/integration/
env:
API_BASE_URL: http://localhost:8080
For deterministic runs (zero network access after the first setup), use apighost replay instead of apighost serve and commit the cassette to the repo.
Point your test client at localhost:8080
Configure your test suite's HTTP client to use the mock server URL instead of the real API. Most frameworks support this via an environment variable:
# pytest (Python)
import os
import httpx
BASE_URL = os.getenv("API_BASE_URL", "http://localhost:8080")
def test_list_users():
resp = httpx.get(f"{BASE_URL}/users")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list)
// Jest (Node.js / TypeScript)
const BASE_URL = process.env.API_BASE_URL ?? "http://localhost:8080";
test("GET /users returns array", async () => {
const res = await fetch(`${BASE_URL}/users`);
expect(res.status).toBe(200);
const data = await res.json();
expect(Array.isArray(data)).toBe(true);
});
APIGhost vs alternatives
The main contenders for OpenAPI-driven mocking are Prism, WireMock, and Mockoon. Here's how they differ:
| Feature | APIGhost | Prism | WireMock | Mockoon |
|---|---|---|---|---|
| OpenAPI → mock server | ✓ | ✓ | ✗ | ✓ |
| VCR record/replay | ✓ | ✗ | ✓ | ✗ |
| Scenario switching | ✓ | ✗ | ✓ | ✓ |
| Realistic fake data | ✓ | ✓ | ✗ | ✗ |
| CLI-first (no GUI) | ✓ | ✓ | ✓ | ✗ |
| Zero-config from spec | ✓ | ✓ | ✗ | ✓ |
| Spec change auto-sync | ✓ | ✓ | ✗ | ✗ |
vs Prism: Prism is the closest alternative — also CLI-first, also OpenAPI-driven. The key differences: Prism has no VCR recording, no named scenario system, and does not generate data from property-name hints. If you need to capture and replay real interactions, or switch between a "success" and "rate-limited" scenario without code changes, APIGhost covers that.
vs WireMock: WireMock requires hand-written stub definitions. Its strength is its recording feature, but without an OpenAPI spec as the source of truth, your stubs drift. APIGhost auto-generates stubs from the spec and keeps them in sync as the spec evolves.
vs Mockoon: Mockoon requires a GUI to configure routes. APIGhost is headless — suitable for CI environments with no display.
Note on install: APIGhost is not yet published to public PyPI. Install via the git URL above or from the self-hosted package index: pip install --index-url https://coding-dev-tools.github.io/pypi-index/simple/ apighost
Pricing
Local and dev use is free with no account required. The Pro tier unlocks unlimited requests and removes the session cap, which matters in CI where a single run can exceed the Free limit quickly:
Free
- Unlimited local use
- 100 requests/session
- OpenAPI serve + scenario
Pro
- Unlimited requests
- VCR cassettes
- CI/CD integration
- $119/yr (save 17%)
Suite
- All Pro features
- Full DevForge suite
- 10 CLI tools
- $39/mo billed annually
Try APIGhost in your next test run
One command to install. One command to start a mock server from your spec. No account required for local use.
MIT license · Python 3.10+ · Compare with Prism, WireMock, Mockoon