A mock server lets a Python application exercise its real HTTP client without calling the real dependency. You control the response, then check that the application sent the expected request and handled the result.
MockServer runs as a separate service. Python is the client in this walkthrough. The examples use requests to call MockServer’s REST API directly, so they do not depend on an additional Python wrapper or an assumed client.expect interface.
Start a disposable MockServer
With Docker running, start a local instance:
docker run -d --rm --name python-mockserver-demo \
-p 127.0.0.1:1080:1080 mockserver/mockserver:5.15.0
This walkthrough pins 5.15.0 to keep its example reproducible; it is not a claim that this is the latest release. For other deployment methods and versions, use the MockServer running guide.
Wait until MockServer is ready:
curl -i -X PUT http://127.0.0.1:1080/mockserver/status
A successful status response shows the server is reachable. If port 1080 is occupied, choose another host port and update the base URL in every example.
Create a Python virtual environment and install the HTTP client:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install requests
Create an expectation and exercise it
Save this as mockserver_demo.py. It clears the dedicated demo instance, registers a GET response, calls it, and checks the result.
import requests
BASE_URL = "http://127.0.0.1:1080"
def reset():
response = requests.put(f"{BASE_URL}/mockserver/reset", timeout=5)
response.raise_for_status()
def expect(request, response):
result = requests.put(
f"{BASE_URL}/mockserver/expectation",
json={"httpRequest": request, "httpResponse": response},
timeout=5,
)
result.raise_for_status()
reset()
expect(
{"method": "GET", "path": "/api/example"},
{
"statusCode": 200,
"headers": {"Content-Type": ["application/json"]},
"body": '{"message": "Hello, MockServer!"}',
},
)
response = requests.get(f"{BASE_URL}/api/example", timeout=5)
assert response.status_code == 200
assert response.json() == {"message": "Hello, MockServer!"}
print("GET expectation passed")
Run it with python mockserver_demo.py. MockServer’s expectation documentation describes the httpRequest matcher and the httpResponse action used here.
In an application test, configure the application’s dependency base URL to point at MockServer, then exercise the application instead of calling the mock directly. A direct request checks the fixture; a request through the application checks its client and error handling.
Test an error response
Append this block to the same file. Resetting first removes the earlier expectation, so the result does not depend on expectation ordering.
reset()
request = {
"method": "POST",
"path": "/external/api",
"body": {"type": "JSON", "json": '{"key": "value"}'},
}
expect(
request,
{
"statusCode": 502,
"headers": {"Content-Type": ["application/json"]},
"body": '{"error": "Bad Gateway"}',
},
)
response = requests.post(
f"{BASE_URL}/external/api", json={"key": "value"}, timeout=5
)
assert response.status_code == 502
assert response.json()["error"] == "Bad Gateway"
print("Error expectation passed")
When testing your application, assert on its observable behavior: a bounded retry count, the documented fallback, or the error returned to its caller. Receiving the configured 502 from MockServer alone does not establish those behaviors.
Verify that the request was received
A configured response does not prove the application used it. Append a verification request after the POST:
verification = requests.put(
f"{BASE_URL}/mockserver/verify",
json={
"httpRequest": request,
"times": {"atLeast": 1, "atMost": 1},
},
timeout=5,
)
verification.raise_for_status()
print("Exactly one matching POST was received")
The verification API checks recorded requests. Match the fields that matter to the behavior you are testing rather than every incidental header.
Test a timeout and clean up
Append a delayed response and use a shorter client timeout:
reset()
expect(
{"method": "GET", "path": "/delayed/response"},
{
"statusCode": 200,
"body": "delayed",
"delay": {"timeUnit": "SECONDS", "value": 2},
},
)
try:
requests.get(f"{BASE_URL}/delayed/response", timeout=0.2)
except requests.exceptions.ReadTimeout:
print("Client timed out as expected")
else:
raise AssertionError("Expected a read timeout")
finally:
reset()
The reset endpoint clears expectations and recorded requests. Use it only on a dedicated test instance; resetting a shared server can break another test. Give parallel tests separate instances or coordinate their state explicitly.
Stop the demo container when finished:
docker stop python-mockserver-demo
When to use recorded mocks
Handwritten expectations work well when a test needs a small, explicit scenario. They still encode your assumptions about the dependency. MockServer also has record-and-replay support, so recording is not exclusive to Speedscale.
If you want a workflow built around recording supported application traffic and reusing it as mocks and regression tests, start with the proxymock local guide. The mocking tools comparison can help you choose the boundary. Keep real-service integration checks for authentication, provider behavior, and contracts that your fixtures cannot establish.