0
# Response Registries
1
2
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.
3
4
## Capabilities
5
6
### FirstMatchRegistry
7
8
The default registry that returns the first response that matches a request. Responses remain available for subsequent matching unless explicitly removed.
9
10
```python { .api }
11
class FirstMatchRegistry:
12
def __init__(self):
13
"""
14
Create a FirstMatchRegistry instance.
15
16
This is the default registry used by responses. When a request
17
is made, it searches through registered responses in order and
18
returns the first one that matches. The response remains in
19
the registry for future matching.
20
"""
21
22
@property
23
def registered(self):
24
"""
25
List of all registered responses.
26
27
Returns:
28
List of BaseResponse objects currently in the registry
29
"""
30
31
def find(self, request):
32
"""
33
Find the first response that matches the request.
34
35
Parameters:
36
- request: PreparedRequest object to match against
37
38
Returns:
39
Tuple of (BaseResponse or None, List[str])
40
- First element: Matching response or None if no match
41
- Second element: List of reasons why other responses didn't match
42
43
The matching response is NOT removed from the registry and can
44
match future requests.
45
"""
46
47
def add(self, response):
48
"""
49
Add a response to the registry.
50
51
Parameters:
52
- response: BaseResponse object to register
53
54
Returns:
55
BaseResponse object that was added (may be a deep copy)
56
57
If the same response instance is added multiple times,
58
it will be deep copied to prevent shared state issues.
59
"""
60
61
def remove(self, response):
62
"""
63
Remove all instances of a response from the registry.
64
65
Parameters:
66
- response: BaseResponse object to remove
67
68
Returns:
69
List of BaseResponse objects that were removed
70
71
Removes all responses that match the given response object.
72
"""
73
74
def replace(self, response):
75
"""
76
Replace an existing response in the registry.
77
78
Parameters:
79
- response: BaseResponse object that should replace existing
80
81
Returns:
82
BaseResponse object representing the replacement
83
84
Raises:
85
ValueError if no matching response is found to replace
86
"""
87
88
def reset(self):
89
"""Clear all registered responses from the registry."""
90
```
91
92
**Usage Example:**
93
94
```python
95
from responses.registries import FirstMatchRegistry
96
97
@responses.activate
98
def test_first_match_behavior():
99
# Multiple responses for same URL
100
responses.add(responses.GET, "http://api.example.com/data", json={"version": 1})
101
responses.add(responses.GET, "http://api.example.com/data", json={"version": 2})
102
103
# First response is always returned
104
resp1 = requests.get("http://api.example.com/data")
105
resp2 = requests.get("http://api.example.com/data")
106
resp3 = requests.get("http://api.example.com/data")
107
108
# All return the first registered response
109
assert resp1.json()["version"] == 1
110
assert resp2.json()["version"] == 1
111
assert resp3.json()["version"] == 1
112
```
113
114
### OrderedRegistry
115
116
A registry that enforces strict ordering where responses must be consumed in the order they were registered. Each response is removed after first use.
117
118
```python { .api }
119
class OrderedRegistry(FirstMatchRegistry):
120
def __init__(self):
121
"""
122
Create an OrderedRegistry instance.
123
124
Responses are consumed in first-in-first-out (FIFO) order.
125
Each response can only be used once and is removed from the
126
registry after matching a request.
127
"""
128
129
def find(self, request):
130
"""
131
Find and remove the next response in order.
132
133
Parameters:
134
- request: PreparedRequest object to match against
135
136
Returns:
137
Tuple of (BaseResponse or None, List[str])
138
- First element: Next response in order if it matches, None otherwise
139
- Second element: List with error message if no match
140
141
The matching response is REMOVED from the registry and cannot
142
match future requests. If the next response doesn't match the
143
request, an error is returned and the registry is reset.
144
145
This enforces that requests must be made in the exact order
146
that responses were registered.
147
"""
148
```
149
150
**Usage Example:**
151
152
```python
153
from responses.registries import OrderedRegistry
154
155
def test_ordered_responses():
156
with responses.RequestsMock() as rsps:
157
# Switch to ordered registry
158
rsps.reset()
159
rsps._set_registry(OrderedRegistry)
160
161
# Register responses in specific order
162
rsps.add(responses.GET, "http://api.example.com/step1", json={"step": 1})
163
rsps.add(responses.POST, "http://api.example.com/step2", json={"step": 2})
164
rsps.add(responses.PUT, "http://api.example.com/step3", json={"step": 3})
165
166
# Requests must be made in exact order
167
step1 = requests.get("http://api.example.com/step1") # Uses first response
168
step2 = requests.post("http://api.example.com/step2") # Uses second response
169
step3 = requests.put("http://api.example.com/step3") # Uses third response
170
171
assert step1.json()["step"] == 1
172
assert step2.json()["step"] == 2
173
assert step3.json()["step"] == 3
174
175
# Registry is now empty - no more responses available
176
with pytest.raises(ConnectionError):
177
requests.get("http://api.example.com/step1") # Would fail
178
```
179
180
### Registry Usage Patterns
181
182
#### Simulating API Sequences
183
184
OrderedRegistry is useful for testing sequences of API calls that must happen in a specific order:
185
186
```python
187
def test_user_registration_flow():
188
with responses.RequestsMock() as rsps:
189
rsps._set_registry(OrderedRegistry)
190
191
# Step 1: Check email availability
192
rsps.add(
193
responses.GET,
194
"http://api.example.com/users/check-email?email=test@example.com",
195
json={"available": True}
196
)
197
198
# Step 2: Create user
199
rsps.add(
200
responses.POST,
201
"http://api.example.com/users",
202
json={"id": 123, "email": "test@example.com"},
203
status=201
204
)
205
206
# Step 3: Send verification email
207
rsps.add(
208
responses.POST,
209
"http://api.example.com/users/123/send-verification",
210
json={"sent": True}
211
)
212
213
# Run the registration flow
214
availability = requests.get("http://api.example.com/users/check-email?email=test@example.com")
215
assert availability.json()["available"] is True
216
217
user = requests.post("http://api.example.com/users", json={"email": "test@example.com"})
218
assert user.status_code == 201
219
220
verification = requests.post("http://api.example.com/users/123/send-verification")
221
assert verification.json()["sent"] is True
222
```
223
224
#### Simulating Different Response Patterns
225
226
FirstMatchRegistry allows the same endpoint to always return the same response:
227
228
```python
229
def test_stable_api_responses():
230
# Using default FirstMatchRegistry
231
responses.add(
232
responses.GET,
233
"http://api.example.com/config",
234
json={"api_version": "v1", "features": ["feature1", "feature2"]}
235
)
236
237
# Multiple calls to same endpoint return same response
238
config1 = requests.get("http://api.example.com/config")
239
config2 = requests.get("http://api.example.com/config")
240
241
assert config1.json() == config2.json()
242
```
243
244
### Registry Selection and Switching
245
246
Control which registry is used for response matching:
247
248
```python { .api }
249
# Using with RequestsMock constructor
250
mock = RequestsMock(registry=OrderedRegistry)
251
252
# Switching registry on existing mock (must reset first)
253
@responses.activate
254
def test_registry_switching():
255
# Start with default FirstMatchRegistry
256
responses.add(responses.GET, "http://api.example.com/test", json={"registry": "first"})
257
258
# Switch to OrderedRegistry
259
responses.reset() # Clear existing responses
260
responses.mock._set_registry(OrderedRegistry)
261
262
# Add new responses to OrderedRegistry
263
responses.add(responses.GET, "http://api.example.com/test", json={"registry": "ordered"})
264
```
265
266
### Custom Registry Implementation
267
268
Advanced users can implement custom registries by extending FirstMatchRegistry:
269
270
```python
271
class CustomRegistry(FirstMatchRegistry):
272
def find(self, request):
273
# Custom matching logic
274
# Must return (response_or_none, reasons_list)
275
pass
276
277
# Use custom registry
278
mock = RequestsMock(registry=CustomRegistry)
279
```
280
281
## Registry Error Handling
282
283
### OrderedRegistry Errors
284
285
OrderedRegistry raises specific errors when requests don't match the expected order:
286
287
```python
288
def test_order_mismatch_error():
289
with responses.RequestsMock() as rsps:
290
rsps._set_registry(OrderedRegistry)
291
292
rsps.add(responses.GET, "http://api.example.com/first", json={"order": 1})
293
rsps.add(responses.GET, "http://api.example.com/second", json={"order": 2})
294
295
# Skip first response and try second - will fail
296
with pytest.raises(ConnectionError) as exc_info:
297
requests.get("http://api.example.com/second")
298
299
error_message = str(exc_info.value)
300
assert "Next 'Response' in the order doesn't match" in error_message
301
```
302
303
### Registry State Management
304
305
```python { .api }
306
def registered():
307
"""
308
Get all currently registered responses.
309
310
Returns:
311
List of BaseResponse objects in the registry
312
313
Useful for debugging and verifying registry state.
314
"""
315
316
def reset():
317
"""
318
Clear all responses from the registry.
319
320
Resets the registry to empty state. Required before
321
switching registry types with _set_registry().
322
"""
323
```
324
325
**Usage Example:**
326
327
```python
328
@responses.activate
329
def test_registry_state():
330
# Add some responses
331
responses.add(responses.GET, "http://api.example.com/1", json={"id": 1})
332
responses.add(responses.GET, "http://api.example.com/2", json={"id": 2})
333
334
# Check registry state
335
registered_responses = responses.registered()
336
assert len(registered_responses) == 2
337
338
# Clear registry
339
responses.reset()
340
assert len(responses.registered()) == 0
341
```