0
# State Inspection
1
2
State inspection and debugging functions for examining mock states, pending mocks, unmatched requests, and engine status. These functions are essential for debugging test failures, verifying mock behavior, and understanding pook's internal state during development and testing.
3
4
## Capabilities
5
6
### Mock State Functions
7
8
Functions for checking the status and completion state of registered mocks.
9
10
```python { .api }
11
def pending():
12
"""
13
Returns the number of pending mocks to be matched.
14
15
Pending mocks are mocks that have been registered but not yet
16
matched by any HTTP requests.
17
18
Returns:
19
int: Number of pending mocks
20
"""
21
22
def ispending():
23
"""
24
Returns the number of pending mocks to be matched (alias to pending()).
25
26
Returns:
27
int: Number of pending mocks
28
"""
29
30
def pending_mocks():
31
"""
32
Returns pending mocks to be matched.
33
34
Returns the actual Mock instances that are still waiting to be
35
matched by HTTP requests.
36
37
Returns:
38
list: List of pending Mock instances
39
"""
40
41
def isdone():
42
"""
43
Returns True if all registered mocks have been triggered.
44
45
A mock engine is considered "done" when all registered mocks
46
have been matched at least once (or up to their times() limit).
47
48
Returns:
49
bool: True if all mocks have been consumed, False otherwise
50
"""
51
```
52
53
Usage examples:
54
55
```python
56
import pook
57
import requests
58
59
# Setup multiple mocks
60
pook.activate()
61
pook.get('https://api.example.com/users').reply(200).json([])
62
pook.get('https://api.example.com/posts').reply(200).json([])
63
pook.post('https://api.example.com/users').reply(201)
64
65
print(f"Pending mocks: {pook.pending()}") # 3
66
print(f"Is pending: {pook.ispending()}") # 3 (alias)
67
print(f"All done: {pook.isdone()}") # False
68
69
# Make some requests
70
requests.get('https://api.example.com/users') # Matches first mock
71
print(f"Pending mocks: {pook.pending()}") # 2
72
print(f"All done: {pook.isdone()}") # False
73
74
requests.get('https://api.example.com/posts') # Matches second mock
75
print(f"Pending mocks: {pook.pending()}") # 1
76
77
requests.post('https://api.example.com/users') # Matches third mock
78
print(f"Pending mocks: {pook.pending()}") # 0
79
print(f"All done: {pook.isdone()}") # True
80
81
# Inspect pending mock details
82
pook.get('https://api.example.com/new').reply(200)
83
pending_list = pook.pending_mocks()
84
print(f"Pending mock URLs: {[mock.request.rawurl for mock in pending_list]}")
85
86
pook.off()
87
```
88
89
### Unmatched Request Functions
90
91
Functions for examining requests that didn't match any registered mocks, primarily useful in networking mode for debugging.
92
93
```python { .api }
94
def unmatched_requests():
95
"""
96
Returns a list of unmatched requests (only available in networking mode).
97
98
When networking mode is enabled, pook tracks requests that didn't
99
match any registered mocks. This is useful for debugging why
100
certain requests weren't intercepted.
101
102
Returns:
103
list: List of unmatched intercepted Request objects
104
"""
105
106
def unmatched():
107
"""
108
Returns the total number of unmatched requests intercepted by pook.
109
110
Returns:
111
int: Total number of unmatched requests
112
"""
113
114
def isunmatched():
115
"""
116
Returns True if there are unmatched requests, otherwise False.
117
118
Returns:
119
bool: True if any requests were intercepted but not matched
120
"""
121
```
122
123
Usage examples:
124
125
```python
126
import pook
127
import requests
128
129
# Enable networking to track unmatched requests
130
pook.activate()
131
pook.enable_network()
132
133
# Register some mocks
134
pook.get('https://api.example.com/users').reply(200).json([])
135
pook.post('https://api.example.com/users').reply(201)
136
137
# Make requests - some match, some don't
138
requests.get('https://api.example.com/users') # Matches mock
139
requests.get('https://api.example.com/posts') # No mock, goes to network
140
requests.delete('https://api.example.com/users/1') # No mock, goes to network
141
142
print(f"Unmatched count: {pook.unmatched()}") # 2
143
print(f"Has unmatched: {pook.isunmatched()}") # True
144
145
# Inspect unmatched requests
146
unmatched_reqs = pook.unmatched_requests()
147
for req in unmatched_reqs:
148
print(f"Unmatched: {req.method} {req.rawurl}")
149
150
# Output:
151
# Unmatched: GET https://api.example.com/posts
152
# Unmatched: DELETE https://api.example.com/users/1
153
154
pook.off()
155
```
156
157
### Engine Status Functions
158
159
Functions for checking the current state and activity status of pook's engine.
160
161
```python { .api }
162
def isactive():
163
"""
164
Returns True if pook is active and intercepting traffic, otherwise False.
165
166
The engine is active when activate() has been called and
167
before disable() or off() is called.
168
169
Returns:
170
bool: Current engine activation status
171
"""
172
```
173
174
Usage examples:
175
176
```python
177
import pook
178
179
print(f"Initially active: {pook.isactive()}") # False
180
181
pook.activate()
182
print(f"After activate: {pook.isactive()}") # True
183
184
pook.disable()
185
print(f"After disable: {pook.isactive()}") # False
186
187
pook.activate()
188
print(f"Reactivated: {pook.isactive()}") # True
189
190
pook.off() # Disable and reset
191
print(f"After off: {pook.isactive()}") # False
192
```
193
194
## Comprehensive Debugging Patterns
195
196
Practical patterns for using state inspection functions to debug complex testing scenarios.
197
198
### Test Assertion Patterns
199
200
```python
201
import pook
202
import requests
203
204
def test_all_mocks_consumed():
205
"""Ensure all expected API calls were made."""
206
207
pook.activate()
208
209
# Setup expected API calls
210
pook.get('https://api.example.com/config').reply(200).json({'version': '1.0'})
211
pook.get('https://api.example.com/users').reply(200).json([])
212
pook.post('https://api.example.com/analytics').reply(201)
213
214
# Run application code that should make these calls
215
# ... application code here ...
216
217
# Verify all mocks were consumed
218
if not pook.isdone():
219
pending = pook.pending_mocks()
220
urls = [mock.request.rawurl for mock in pending]
221
raise AssertionError(f"Unconsumed mocks: {urls}")
222
223
pook.off()
224
225
def test_no_unexpected_requests():
226
"""Ensure no unexpected network requests were made."""
227
228
pook.activate()
229
pook.enable_network()
230
231
# Setup expected mocks
232
pook.get('https://api.example.com/expected').reply(200)
233
234
# Run application code
235
# ... application code here ...
236
237
# Check for unexpected requests
238
if pook.isunmatched():
239
unmatched = pook.unmatched_requests()
240
unexpected = [f"{req.method} {req.rawurl}" for req in unmatched]
241
raise AssertionError(f"Unexpected requests: {unexpected}")
242
243
pook.off()
244
```
245
246
### Mock Lifecycle Monitoring
247
248
```python
249
import pook
250
import requests
251
252
class MockMonitor:
253
"""Helper class for monitoring mock state during tests."""
254
255
def __init__(self):
256
self.snapshots = []
257
258
def snapshot(self, label):
259
"""Take a snapshot of current mock state."""
260
snapshot = {
261
'label': label,
262
'active': pook.isactive(),
263
'pending': pook.pending(),
264
'unmatched': pook.unmatched(),
265
'done': pook.isdone()
266
}
267
self.snapshots.append(snapshot)
268
return snapshot
269
270
def report(self):
271
"""Generate a report of all snapshots."""
272
for snap in self.snapshots:
273
print(f"{snap['label']}: active={snap['active']}, "
274
f"pending={snap['pending']}, unmatched={snap['unmatched']}, "
275
f"done={snap['done']}")
276
277
# Usage
278
monitor = MockMonitor()
279
280
pook.activate()
281
pook.enable_network()
282
monitor.snapshot("After activation")
283
284
# Setup mocks
285
pook.get('https://api.example.com/step1').reply(200).json({'step': 1})
286
pook.get('https://api.example.com/step2').reply(200).json({'step': 2})
287
monitor.snapshot("After mock setup")
288
289
# Execute step 1
290
requests.get('https://api.example.com/step1')
291
monitor.snapshot("After step 1")
292
293
# Execute step 2
294
requests.get('https://api.example.com/step2')
295
monitor.snapshot("After step 2")
296
297
# Make unexpected request
298
requests.get('https://api.example.com/unexpected')
299
monitor.snapshot("After unexpected request")
300
301
monitor.report()
302
pook.off()
303
304
# Output:
305
# After activation: active=True, pending=0, unmatched=0, done=True
306
# After mock setup: active=True, pending=2, unmatched=0, done=False
307
# After step 1: active=True, pending=1, unmatched=0, done=False
308
# After step 2: active=True, pending=0, unmatched=0, done=True
309
# After unexpected request: active=True, pending=0, unmatched=1, done=True
310
```
311
312
### Detailed Mock Inspection
313
314
```python
315
import pook
316
import requests
317
318
def inspect_mock_details():
319
"""Detailed inspection of individual mock states."""
320
321
pook.activate()
322
323
# Create mocks with different configurations
324
mock1 = pook.get('https://api.example.com/limited').times(2).reply(200)
325
mock2 = pook.get('https://api.example.com/persistent').persist().reply(200)
326
mock3 = pook.get('https://api.example.com/unused').reply(200)
327
328
def print_mock_state(mock, name):
329
print(f"{name}:")
330
print(f" Done: {mock.isdone()}")
331
print(f" Matched: {mock.ismatched()}")
332
print(f" Total matches: {mock.total_matches}")
333
print(f" Calls: {mock.calls}")
334
335
print("Initial state:")
336
print_mock_state(mock1, "Limited mock (2 times)")
337
print_mock_state(mock2, "Persistent mock")
338
print_mock_state(mock3, "Unused mock")
339
340
# Make some requests
341
requests.get('https://api.example.com/limited') # First hit
342
requests.get('https://api.example.com/persistent') # First hit
343
344
print("\nAfter first requests:")
345
print_mock_state(mock1, "Limited mock")
346
print_mock_state(mock2, "Persistent mock")
347
print_mock_state(mock3, "Unused mock")
348
349
requests.get('https://api.example.com/limited') # Second hit (expires)
350
requests.get('https://api.example.com/persistent') # Second hit (continues)
351
352
print("\nAfter second requests:")
353
print_mock_state(mock1, "Limited mock")
354
print_mock_state(mock2, "Persistent mock")
355
print_mock_state(mock3, "Unused mock")
356
357
print(f"\nOverall state:")
358
print(f" Total pending: {pook.pending()}")
359
print(f" All done: {pook.isdone()}")
360
361
# List remaining pending mocks
362
pending = pook.pending_mocks()
363
if pending:
364
print(f" Pending mock URLs: {[m.request.rawurl for m in pending]}")
365
366
pook.off()
367
```
368
369
### Error Debugging Helpers
370
371
```python
372
import pook
373
import requests
374
375
def debug_mock_matching():
376
"""Helper for debugging why requests aren't matching mocks."""
377
378
pook.activate()
379
380
# Setup a mock with specific criteria
381
mock = pook.post('https://api.example.com/users') \
382
.header('Content-Type', 'application/json') \
383
.json({'name': 'John', 'age': 30}) \
384
.reply(201)
385
386
print(f"Mock registered. Pending: {pook.pending()}")
387
388
# Make a request that might not match
389
try:
390
response = requests.post('https://api.example.com/users',
391
json={'name': 'John', 'age': '30'}, # age as string
392
headers={'Content-Type': 'application/json'})
393
print(f"Request succeeded: {response.status_code}")
394
except Exception as e:
395
print(f"Request failed: {e}")
396
397
# Check what happened
398
print(f"After request - Pending: {pook.pending()}")
399
print(f"Mock matched: {mock.ismatched()}")
400
401
if not mock.ismatched():
402
print("Mock was not matched. Possible issues:")
403
print("- Request body didn't match expected JSON structure")
404
print("- Headers didn't match")
405
print("- URL didn't match")
406
print("- HTTP method didn't match")
407
408
pook.off()
409
410
def validate_test_cleanup():
411
"""Ensure proper test cleanup and isolation."""
412
413
def run_test():
414
pook.activate()
415
pook.get('https://api.example.com/test').reply(200)
416
# Simulate test that forgets cleanup
417
# pook.off() # Commented out - simulates forgotten cleanup
418
419
# Before test
420
print(f"Before test - Active: {pook.isactive()}, Pending: {pook.pending()}")
421
422
run_test()
423
424
# After test
425
print(f"After test - Active: {pook.isactive()}, Pending: {pook.pending()}")
426
427
if pook.isactive() or pook.pending() > 0:
428
print("WARNING: Test didn't clean up properly!")
429
print("This could affect other tests.")
430
pook.off() # Force cleanup
431
432
print(f"After cleanup - Active: {pook.isactive()}, Pending: {pook.pending()}")
433
```
434
435
## MatcherEngine Inspection
436
437
```python { .api }
438
class MatcherEngine:
439
"""
440
HTTP request matcher engine used by Mock to test if an intercepted
441
outgoing HTTP request should be mocked out.
442
"""
443
444
def match(self, request):
445
"""
446
Matches HTTP request against registered matchers.
447
448
Parameters:
449
- request: HTTP request to match
450
451
Returns:
452
tuple: (bool, list[str]) - Match success and error messages
453
"""
454
```
455
456
Usage examples:
457
458
```python
459
import pook
460
461
# Access matcher engine for detailed debugging
462
mock = pook.get('https://api.example.com/test')
463
mock.json({'required': 'field'}).reply(200)
464
465
# Simulate a request for testing
466
from pook import Request
467
test_request = Request(
468
method='GET',
469
url='https://api.example.com/test',
470
json={'different': 'field'}
471
)
472
473
# Test if request would match
474
success, errors = mock._matchers.match(test_request)
475
print(f"Would match: {success}")
476
if errors:
477
print(f"Match errors: {errors}")
478
```