DEV Community

nk sk
nk sk

Posted on

🚀 The Complete Guide to Building Mock Servers in Python

When developing modern applications, it’s common to hit a roadblock because the real backend service isn’t ready, or you need to test against multiple APIs at once. That’s where a mock server comes in: a lightweight server that returns fake data and files to mimic the behavior of your real backend.

Python is an excellent choice for building mock servers because:

  • It ships with a built-in HTTP server.
  • It has lightweight frameworks like Flask and FastAPI.
  • It can scale from a quick one-liner to a production-like simulation.

In this guide, we’ll cover all Python-based options — from serving static files, to full CRUD APIs, to enterprise-style mocking.


🔹 1. The Easiest Option: Python’s Built-in HTTP Server

Python ships with http.server, a simple web server module. It’s the fastest way to expose a directory of files over HTTP.

One-liner:

python3 -m http.server 8080 --directory ./mocks
Enter fullscreen mode Exit fullscreen mode
  • Serves static files (./mocks/users.json → http://localhost:8080/users.json).
  • Supports file download (for .pdf, .zip, etc.).
  • Useful for quick frontend testing.

👉 Options you can use:

  • port → change port (default 8000).
  • --directory → serve a different folder.
  • --bind → bind to specific IP (e.g., 127.0.0.1).
  • --cgi → enable CGI scripts for basic dynamic responses.

âś… Best for: serving static JSONs, files, or mock downloads.
❌ Limitations: no dynamic routes, no CRUD.


🔹 2. Extending http.server with Custom Handlers

If you need basic API-like behavior, subclass BaseHTTPRequestHandler.

from http.server import HTTPServer, BaseHTTPRequestHandler
import json

users = [{"id": 1, "name": "Alice"}]

class MockHandler(BaseHTTPRequestHandler):
    def _send_json(self, data, status=200):
        self.send_response(status)
        self.send_header("Content-Type", "application/json")
        self.end_headers()
        self.wfile.write(json.dumps(data).encode())

    def do_GET(self):
        if self.path == "/api/users":
            self._send_json(users)
        else:
            self._send_json({"error": "Not found"}, 404)

    def do_POST(self):
        if self.path == "/api/users":
            length = int(self.headers["Content-Length"])
            body = json.loads(self.rfile.read(length))
            users.append(body)
            self._send_json(body, 201)

httpd = HTTPServer(("localhost", 8080), MockHandler)
print("Mock server running at http://localhost:8080")
httpd.serve_forever()
Enter fullscreen mode Exit fullscreen mode

âś… Supports:

  • GET → returns JSON
  • POST → add data
  • Extendable with PUT/DELETE

❌ Limitations:

  • In-memory only (data resets on restart).
  • More coding effort than Flask/FastAPI.

🔹 3. Flask for Flexible Mock APIs

Flask is a minimal Python web framework that makes CRUD APIs + file serving simple.

from flask import Flask, jsonify, request, send_from_directory
import os

app = Flask(__name__)
users = [{"id": 1, "name": "Alice"}]

# --- CRUD Endpoints ---
@app.route("/api/users", methods=["GET"])
def get_users():
    return jsonify(users)

@app.route("/api/users", methods=["POST"])
def add_user():
    user = request.json
    users.append(user)
    return jsonify(user), 201

@app.route("/api/users/<int:user_id>", methods=["PUT"])
def update_user(user_id):
    for u in users:
        if u["id"] == user_id:
            u.update(request.json)
            return jsonify(u)
    return jsonify({"error": "Not found"}), 404

@app.route("/api/users/<int:user_id>", methods=["DELETE"])
def delete_user(user_id):
    global users
    users = [u for u in users if u["id"] != user_id]
    return jsonify({"message": "deleted"})

# --- File Download ---
@app.route("/files/<path:filename>")
def download_file(filename):
    return send_from_directory("files", filename)

if __name__ == "__main__":
    os.makedirs("files", exist_ok=True)
    app.run(port=5000, debug=True)
Enter fullscreen mode Exit fullscreen mode

âś… Features:

  • Full CRUD (GET/POST/PUT/DELETE).
  • File download support (/files/sample.pdf).
  • Easy to add new routes.

❌ Limitation: not type-safe, no auto-generated docs.


🔹 4. FastAPI for Production-Like Mock Servers

FastAPI is a modern Python framework with async support, OpenAPI, and Swagger docs. Perfect for mocks that look like a real backend.

from fastapi import FastAPI
from fastapi.responses import FileResponse
from pydantic import BaseModel
import os

app = FastAPI()

class User(BaseModel):
    id: int
    name: str

users = [User(id=1, name="Alice")]

@app.get("/api/users")
def get_users():
    return users

@app.post("/api/users")
def add_user(user: User):
    users.append(user)
    return user

@app.put("/api/users/{user_id}")
def update_user(user_id: int, user: User):
    for i, u in enumerate(users):
        if u.id == user_id:
            users[i] = user
            return user
    return {"error": "Not found"}

@app.delete("/api/users/{user_id}")
def delete_user(user_id: int):
    global users
    users = [u for u in users if u.id != user_id]
    return {"message": "deleted"}

@app.get("/files/{filename}")
def download_file(filename: str):
    return FileResponse(f"files/{filename}")

if __name__ == "__main__":
    os.makedirs("files", exist_ok=True)
Enter fullscreen mode Exit fullscreen mode

Run:

uvicorn mock_server:app --reload --port 8000
Enter fullscreen mode Exit fullscreen mode

✨ Auto-generated Swagger UI:
👉 http://localhost:8000/docs

âś… Features:

  • Full CRUD with schema validation.
  • File serving (/files/<filename>).
  • Auto-generated OpenAPI/Swagger docs.
  • Type-safe with Pydantic models.

❌ Limitation: requires fastapi + uvicorn install.


🔹 5. Test-Only Mocking (No Server Needed)

If you’re writing Python tests and don’t need an actual server:

  • responses library (intercepts requests calls).
  • requests-mock (pytest-friendly).

Example with responses:

import responses, requests

@responses.activate
def test_mock():
    responses.add(
        responses.GET, "http://api.test/users",
        json=[{"id": 1, "name": "Alice"}], status=200
    )
    resp = requests.get("http://api.test/users")
    assert resp.json()[0]["name"] == "Alice"
Enter fullscreen mode Exit fullscreen mode

âś… Best for: unit tests.
❌ Not usable by frontends (since no actual server runs).


🔹 6. Running Multiple Mock Servers

If your app depends on multiple services:

Option A: Multiple http.server instances

python3 -m http.server 8001 --directory ./service1
python3 -m http.server 8002 --directory ./service2
Enter fullscreen mode Exit fullscreen mode

Option B: Multiple Flask/FastAPI apps

Run different servers on different ports.

Option C: Docker Compose

Define multiple mock services in docker-compose.yml:

version: '3'
services:
  users:
    build: ./users-mock
    ports: ["8001:5000"]
  orders:
    build: ./orders-mock
    ports: ["8002:5000"]
Enter fullscreen mode Exit fullscreen mode

âś… Perfect for microservices testing.


🔹 7. Enhancing Mocks with Realism

A mock server shouldn’t just return “happy path” responses. You can make it more production-like by:

  • Randomized fake data → use faker library.
  • Error simulation → sometimes return 500, 404, or slow responses.
  • Versioning APIs → serve /v1/users.json vs /v2/users.json.
  • Environment configs → switch between real and mock easily.

🔹 Summary: When to Use Which

Approach Best For Pros Cons
http.server Static files, quick downloads Zero setup No dynamic logic
Custom BaseHTTPRequestHandler Lightweight custom JSON APIs Built-in, simple Harder to extend
Flask CRUD + file serving Easy, readable No schema validation
FastAPI Realistic mocks, Swagger docs Modern, OpenAPI Needs more setup
responses / requests-mock Unit tests (no server) Simple, fast Only inside Python tests
Multi-instance/Docker mocks Microservices Scales well Extra setup

âś… Final Thoughts

Mock servers in Python can be as simple as:

  • python3 -m http.server for files and JSON.
  • Flask for CRUD APIs + downloads.
  • FastAPI for realistic, contract-driven mocks.
  • Or even responses for test-only mocks.

By combining these approaches, you can create a mock ecosystem that unblocks frontend developers, enables CI/CD testing, and simulates multiple backend services — all with Python.

Top comments (0)