Testing & Mocking

Mock Servers for API Development: WireMock, Prism, and Custom Mocks

How to build and use mock servers that return predictable status codes — WireMock, Prism (OpenAPI), json-server, and custom mock servers for development and testing.

Why Mock Servers

Mock servers solve a fundamental coordination problem in API development: the frontend team needs the API to exist before the backend team has built it. They also enable testing error scenarios that are hard to trigger reliably against a real service — 503s, 429s, network timeouts, malformed responses.

A mock server intercepts HTTP requests and returns pre-configured responses. The calling code cannot tell the difference. This unlocks:

  • Parallel development: Frontend and backend teams work simultaneously
  • Offline development: No VPN, no staging environment required
  • Deterministic tests: No flaky network calls, no external state
  • Error simulation: Return any status code on demand

WireMock

WireMock is the most feature-rich mock server for HTTP. It supports request matching, response templating, stateful scenarios, fault injection, and recording/replay.

Quick Start

# Run WireMock standalone
docker run -it --rm -p 8080:8080 wiremock/wiremock

# Or with Java
java -jar wiremock-standalone.jar --port 8080

Stub Mappings

Stubs are JSON files that map request patterns to responses:

{
  "request": {
    "method": "GET",
    "urlPattern": "/users/[0-9]+"
  },
  "response": {
    "status": 200,
    "headers": { "Content-Type": "application/json" },
    "jsonBody": { "id": 42, "name": "Alice", "email": "[email protected]" }
  }
}

Fault Injection

WireMock can simulate network-level faults, not just HTTP errors:

{
  "request": { "method": "GET", "url": "/slow-endpoint" },
  "response": {
    "status": 503,
    "fixedDelayMilliseconds": 5000,
    "jsonBody": { "error": "Service temporarily unavailable" }
  }
}

For connection-level faults:

{
  "request": { "method": "POST", "url": "/payments" },
  "response": {
    "fault": "CONNECTION_RESET_BY_PEER"
  }
}

Stateful Scenarios

WireMock scenarios let you model stateful interactions:

[
  {
    "scenarioName": "Order Flow",
    "requiredScenarioState": "Started",
    "newScenarioState": "OrderPlaced",
    "request": { "method": "POST", "url": "/orders" },
    "response": { "status": 201, "jsonBody": { "id": 1, "status": "pending" } }
  },
  {
    "scenarioName": "Order Flow",
    "requiredScenarioState": "OrderPlaced",
    "request": { "method": "GET", "url": "/orders/1" },
    "response": { "status": 200, "jsonBody": { "id": 1, "status": "pending" } }
  }
]

WireMock in Python Tests

import pytest
import requests
from wiremock.client import WireMock, Mapping, Request, Response

@pytest.fixture(autouse=True)
def wiremock():
    wm = WireMock(host='localhost', port=8080)
    wm.reset()
    yield wm
    wm.reset()

def test_payment_gateway_failure(wiremock):
    wiremock.stub(
        Mapping(
            request=Request(method='POST', url='/charge'),
            response=Response(status=502, json_body={'error': 'Gateway error'})
        )
    )
    result = payment_service.charge(amount=100)
    assert result.success is False
    assert result.retry_after is not None

Prism (OpenAPI Mock Server)

Prism by Stoplight generates a mock server directly from your OpenAPI specification. Any endpoint defined in the spec becomes immediately available with realistic responses.

# Install
npm install -g @stoplight/prism-cli

# Run mock server from local spec
prism mock openapi.yaml

# Run against a remote spec
prism mock https://api.example.com/openapi.yaml

Validation Mode

Prism can also act as a validation proxy — forwarding requests to your real API but validating both the request and response against the spec:

prism proxy openapi.yaml https://api.staging.example.com --errors

Any response from your API that doesn't match the spec returns a 500 with a validation error. This catches spec drift before it reaches clients.

Dynamic Responses

By default Prism returns examples defined in your OpenAPI spec. Use the -d flag for dynamically generated responses based on schemas:

prism mock openapi.yaml --dynamic

json-server

json-server creates a full REST API from a single JSON file with zero code:

npm install -g json-server

Create db.json:

{
  "users": [
    { "id": 1, "name": "Alice", "email": "[email protected]" },
    { "id": 2, "name": "Bob", "email": "[email protected]" }
  ],
  "orders": [
    { "id": 1, "userId": 1, "total": 4999, "status": "shipped" }
  ]
}
json-server --watch db.json --port 3001
# GET /users → 200 with array
# GET /users/1 → 200 with object
# POST /users → 201 with created object
# DELETE /users/1 → 200

Custom Status Codes in json-server

Add middleware to return custom status codes:

// middleware.js
module.exports = (req, res, next) => {
  if (req.path === '/payments' && req.method === 'POST') {
    return res.status(422).json({ error: 'Card declined', code: 'card_declined' });
  }
  next();
};
json-server --watch db.json --middlewares middleware.js

Custom Mock Servers

Sometimes you need more control than WireMock or Prism provide. A custom mock server in Express or FastAPI gives you full flexibility:

# FastAPI mock server
from fastapi import FastAPI, Response
from typing import Any
import random

app = FastAPI()

ERROR_RATE = 0.1  # 10% of requests return 500

@app.post('/payments/charge')
def charge_payment(body: dict[str, Any]) -> Response:
    if random.random() < ERROR_RATE:
        return Response(status_code=502, content='{"error": "Gateway timeout"}')
    if body.get('amount', 0) > 100000:
        return Response(status_code=422, content='{"error": "Amount exceeds limit"}')
    return Response(
        status_code=200,
        content='{"charge_id": "ch_test_123", "status": "succeeded"}'
    )

Recording Proxy

A recording proxy captures real API traffic and stores it for replay:

# WireMock in record mode
java -jar wiremock-standalone.jar --proxy-all https://api.stripe.com --record-mappings

After recording, stop the proxy and run in stub mode. All recorded interactions are replayed deterministically — no network required.

Choosing a Mock Tool

ToolBest For
WireMockComplex matching, stateful scenarios, fault injection
PrismOpenAPI-first teams, spec validation, zero-config mocking
json-serverSimple REST CRUD, rapid prototyping
Custom serverPrecise control, probabilistic failures, custom logic

Related Protocols

Related Glossary Terms

More in Testing & Mocking