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
- 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()
âś… 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)
âś… 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)
Run:
uvicorn mock_server:app --reload --port 8000
✨ 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 (interceptsrequests
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"
âś… 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
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"]
âś… 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)