A utility library for mocking out the requests Python library
—
Registry systems that control how responses are matched and selected when multiple responses could potentially match a request. Different registry types provide different strategies for response selection, enabling complex testing scenarios with precise control over response ordering and reuse.
The default registry that returns the first response that matches a request. Responses remain available for subsequent matching unless explicitly removed.
class FirstMatchRegistry:
def __init__(self):
"""
Create a FirstMatchRegistry instance.
This is the default registry used by responses. When a request
is made, it searches through registered responses in order and
returns the first one that matches. The response remains in
the registry for future matching.
"""
@property
def registered(self):
"""
List of all registered responses.
Returns:
List of BaseResponse objects currently in the registry
"""
def find(self, request):
"""
Find the first response that matches the request.
Parameters:
- request: PreparedRequest object to match against
Returns:
Tuple of (BaseResponse or None, List[str])
- First element: Matching response or None if no match
- Second element: List of reasons why other responses didn't match
The matching response is NOT removed from the registry and can
match future requests.
"""
def add(self, response):
"""
Add a response to the registry.
Parameters:
- response: BaseResponse object to register
Returns:
BaseResponse object that was added (may be a deep copy)
If the same response instance is added multiple times,
it will be deep copied to prevent shared state issues.
"""
def remove(self, response):
"""
Remove all instances of a response from the registry.
Parameters:
- response: BaseResponse object to remove
Returns:
List of BaseResponse objects that were removed
Removes all responses that match the given response object.
"""
def replace(self, response):
"""
Replace an existing response in the registry.
Parameters:
- response: BaseResponse object that should replace existing
Returns:
BaseResponse object representing the replacement
Raises:
ValueError if no matching response is found to replace
"""
def reset(self):
"""Clear all registered responses from the registry."""Usage Example:
from responses.registries import FirstMatchRegistry
@responses.activate
def test_first_match_behavior():
# Multiple responses for same URL
responses.add(responses.GET, "http://api.example.com/data", json={"version": 1})
responses.add(responses.GET, "http://api.example.com/data", json={"version": 2})
# First response is always returned
resp1 = requests.get("http://api.example.com/data")
resp2 = requests.get("http://api.example.com/data")
resp3 = requests.get("http://api.example.com/data")
# All return the first registered response
assert resp1.json()["version"] == 1
assert resp2.json()["version"] == 1
assert resp3.json()["version"] == 1A registry that enforces strict ordering where responses must be consumed in the order they were registered. Each response is removed after first use.
class OrderedRegistry(FirstMatchRegistry):
def __init__(self):
"""
Create an OrderedRegistry instance.
Responses are consumed in first-in-first-out (FIFO) order.
Each response can only be used once and is removed from the
registry after matching a request.
"""
def find(self, request):
"""
Find and remove the next response in order.
Parameters:
- request: PreparedRequest object to match against
Returns:
Tuple of (BaseResponse or None, List[str])
- First element: Next response in order if it matches, None otherwise
- Second element: List with error message if no match
The matching response is REMOVED from the registry and cannot
match future requests. If the next response doesn't match the
request, an error is returned and the registry is reset.
This enforces that requests must be made in the exact order
that responses were registered.
"""Usage Example:
from responses.registries import OrderedRegistry
def test_ordered_responses():
with responses.RequestsMock() as rsps:
# Switch to ordered registry
rsps.reset()
rsps._set_registry(OrderedRegistry)
# Register responses in specific order
rsps.add(responses.GET, "http://api.example.com/step1", json={"step": 1})
rsps.add(responses.POST, "http://api.example.com/step2", json={"step": 2})
rsps.add(responses.PUT, "http://api.example.com/step3", json={"step": 3})
# Requests must be made in exact order
step1 = requests.get("http://api.example.com/step1") # Uses first response
step2 = requests.post("http://api.example.com/step2") # Uses second response
step3 = requests.put("http://api.example.com/step3") # Uses third response
assert step1.json()["step"] == 1
assert step2.json()["step"] == 2
assert step3.json()["step"] == 3
# Registry is now empty - no more responses available
with pytest.raises(ConnectionError):
requests.get("http://api.example.com/step1") # Would failOrderedRegistry is useful for testing sequences of API calls that must happen in a specific order:
def test_user_registration_flow():
with responses.RequestsMock() as rsps:
rsps._set_registry(OrderedRegistry)
# Step 1: Check email availability
rsps.add(
responses.GET,
"http://api.example.com/users/check-email?email=test@example.com",
json={"available": True}
)
# Step 2: Create user
rsps.add(
responses.POST,
"http://api.example.com/users",
json={"id": 123, "email": "test@example.com"},
status=201
)
# Step 3: Send verification email
rsps.add(
responses.POST,
"http://api.example.com/users/123/send-verification",
json={"sent": True}
)
# Run the registration flow
availability = requests.get("http://api.example.com/users/check-email?email=test@example.com")
assert availability.json()["available"] is True
user = requests.post("http://api.example.com/users", json={"email": "test@example.com"})
assert user.status_code == 201
verification = requests.post("http://api.example.com/users/123/send-verification")
assert verification.json()["sent"] is TrueFirstMatchRegistry allows the same endpoint to always return the same response:
def test_stable_api_responses():
# Using default FirstMatchRegistry
responses.add(
responses.GET,
"http://api.example.com/config",
json={"api_version": "v1", "features": ["feature1", "feature2"]}
)
# Multiple calls to same endpoint return same response
config1 = requests.get("http://api.example.com/config")
config2 = requests.get("http://api.example.com/config")
assert config1.json() == config2.json()Control which registry is used for response matching:
# Using with RequestsMock constructor
mock = RequestsMock(registry=OrderedRegistry)
# Switching registry on existing mock (must reset first)
@responses.activate
def test_registry_switching():
# Start with default FirstMatchRegistry
responses.add(responses.GET, "http://api.example.com/test", json={"registry": "first"})
# Switch to OrderedRegistry
responses.reset() # Clear existing responses
responses.mock._set_registry(OrderedRegistry)
# Add new responses to OrderedRegistry
responses.add(responses.GET, "http://api.example.com/test", json={"registry": "ordered"})Advanced users can implement custom registries by extending FirstMatchRegistry:
class CustomRegistry(FirstMatchRegistry):
def find(self, request):
# Custom matching logic
# Must return (response_or_none, reasons_list)
pass
# Use custom registry
mock = RequestsMock(registry=CustomRegistry)OrderedRegistry raises specific errors when requests don't match the expected order:
def test_order_mismatch_error():
with responses.RequestsMock() as rsps:
rsps._set_registry(OrderedRegistry)
rsps.add(responses.GET, "http://api.example.com/first", json={"order": 1})
rsps.add(responses.GET, "http://api.example.com/second", json={"order": 2})
# Skip first response and try second - will fail
with pytest.raises(ConnectionError) as exc_info:
requests.get("http://api.example.com/second")
error_message = str(exc_info.value)
assert "Next 'Response' in the order doesn't match" in error_messagedef registered():
"""
Get all currently registered responses.
Returns:
List of BaseResponse objects in the registry
Useful for debugging and verifying registry state.
"""
def reset():
"""
Clear all responses from the registry.
Resets the registry to empty state. Required before
switching registry types with _set_registry().
"""Usage Example:
@responses.activate
def test_registry_state():
# Add some responses
responses.add(responses.GET, "http://api.example.com/1", json={"id": 1})
responses.add(responses.GET, "http://api.example.com/2", json={"id": 2})
# Check registry state
registered_responses = responses.registered()
assert len(registered_responses) == 2
# Clear registry
responses.reset()
assert len(responses.registered()) == 0Install with Tessl CLI
npx tessl i tessl/pypi-responses