CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pook

HTTP traffic mocking and expectations made easy for Python testing and development

Pending
Overview
Eval results
Files

state-inspection.mddocs/

State Inspection

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.

Capabilities

Mock State Functions

Functions for checking the status and completion state of registered mocks.

def pending():
    """
    Returns the number of pending mocks to be matched.
    
    Pending mocks are mocks that have been registered but not yet
    matched by any HTTP requests.
    
    Returns:
    int: Number of pending mocks
    """

def ispending():
    """
    Returns the number of pending mocks to be matched (alias to pending()).
    
    Returns:
    int: Number of pending mocks
    """

def pending_mocks():
    """
    Returns pending mocks to be matched.
    
    Returns the actual Mock instances that are still waiting to be
    matched by HTTP requests.
    
    Returns:
    list: List of pending Mock instances
    """

def isdone():
    """
    Returns True if all registered mocks have been triggered.
    
    A mock engine is considered "done" when all registered mocks
    have been matched at least once (or up to their times() limit).
    
    Returns:
    bool: True if all mocks have been consumed, False otherwise
    """

Usage examples:

import pook
import requests

# Setup multiple mocks
pook.activate()
pook.get('https://api.example.com/users').reply(200).json([])
pook.get('https://api.example.com/posts').reply(200).json([])
pook.post('https://api.example.com/users').reply(201)

print(f"Pending mocks: {pook.pending()}")        # 3
print(f"Is pending: {pook.ispending()}")         # 3 (alias)
print(f"All done: {pook.isdone()}")              # False

# Make some requests  
requests.get('https://api.example.com/users')     # Matches first mock
print(f"Pending mocks: {pook.pending()}")        # 2
print(f"All done: {pook.isdone()}")              # False

requests.get('https://api.example.com/posts')    # Matches second mock
print(f"Pending mocks: {pook.pending()}")        # 1

requests.post('https://api.example.com/users')   # Matches third mock
print(f"Pending mocks: {pook.pending()}")        # 0
print(f"All done: {pook.isdone()}")              # True

# Inspect pending mock details
pook.get('https://api.example.com/new').reply(200)
pending_list = pook.pending_mocks()
print(f"Pending mock URLs: {[mock.request.rawurl for mock in pending_list]}")

pook.off()

Unmatched Request Functions

Functions for examining requests that didn't match any registered mocks, primarily useful in networking mode for debugging.

def unmatched_requests():
    """
    Returns a list of unmatched requests (only available in networking mode).
    
    When networking mode is enabled, pook tracks requests that didn't
    match any registered mocks. This is useful for debugging why
    certain requests weren't intercepted.
    
    Returns:
    list: List of unmatched intercepted Request objects
    """

def unmatched():
    """
    Returns the total number of unmatched requests intercepted by pook.
    
    Returns:
    int: Total number of unmatched requests
    """

def isunmatched():
    """
    Returns True if there are unmatched requests, otherwise False.
    
    Returns:
    bool: True if any requests were intercepted but not matched
    """

Usage examples:

import pook
import requests

# Enable networking to track unmatched requests
pook.activate()
pook.enable_network()

# Register some mocks
pook.get('https://api.example.com/users').reply(200).json([])
pook.post('https://api.example.com/users').reply(201)

# Make requests - some match, some don't
requests.get('https://api.example.com/users')      # Matches mock
requests.get('https://api.example.com/posts')      # No mock, goes to network
requests.delete('https://api.example.com/users/1') # No mock, goes to network

print(f"Unmatched count: {pook.unmatched()}")      # 2
print(f"Has unmatched: {pook.isunmatched()}")      # True

# Inspect unmatched requests
unmatched_reqs = pook.unmatched_requests()
for req in unmatched_reqs:
    print(f"Unmatched: {req.method} {req.rawurl}")

# Output:
# Unmatched: GET https://api.example.com/posts  
# Unmatched: DELETE https://api.example.com/users/1

pook.off()

Engine Status Functions

Functions for checking the current state and activity status of pook's engine.

def isactive():
    """
    Returns True if pook is active and intercepting traffic, otherwise False.
    
    The engine is active when activate() has been called and
    before disable() or off() is called.
    
    Returns:
    bool: Current engine activation status
    """

Usage examples:

import pook

print(f"Initially active: {pook.isactive()}")     # False

pook.activate()
print(f"After activate: {pook.isactive()}")       # True

pook.disable()  
print(f"After disable: {pook.isactive()}")        # False

pook.activate()
print(f"Reactivated: {pook.isactive()}")          # True

pook.off()  # Disable and reset
print(f"After off: {pook.isactive()}")            # False

Comprehensive Debugging Patterns

Practical patterns for using state inspection functions to debug complex testing scenarios.

Test Assertion Patterns

import pook
import requests

def test_all_mocks_consumed():
    """Ensure all expected API calls were made."""
    
    pook.activate()
    
    # Setup expected API calls
    pook.get('https://api.example.com/config').reply(200).json({'version': '1.0'})
    pook.get('https://api.example.com/users').reply(200).json([])
    pook.post('https://api.example.com/analytics').reply(201)
    
    # Run application code that should make these calls
    # ... application code here ...
    
    # Verify all mocks were consumed
    if not pook.isdone():
        pending = pook.pending_mocks()
        urls = [mock.request.rawurl for mock in pending]
        raise AssertionError(f"Unconsumed mocks: {urls}")
    
    pook.off()

def test_no_unexpected_requests():
    """Ensure no unexpected network requests were made."""
    
    pook.activate()
    pook.enable_network()
    
    # Setup expected mocks
    pook.get('https://api.example.com/expected').reply(200)
    
    # Run application code
    # ... application code here ...
    
    # Check for unexpected requests
    if pook.isunmatched():
        unmatched = pook.unmatched_requests()
        unexpected = [f"{req.method} {req.rawurl}" for req in unmatched]
        raise AssertionError(f"Unexpected requests: {unexpected}")
    
    pook.off()

Mock Lifecycle Monitoring

import pook
import requests

class MockMonitor:
    """Helper class for monitoring mock state during tests."""
    
    def __init__(self):
        self.snapshots = []
    
    def snapshot(self, label):
        """Take a snapshot of current mock state."""
        snapshot = {
            'label': label,
            'active': pook.isactive(),
            'pending': pook.pending(),
            'unmatched': pook.unmatched(),
            'done': pook.isdone()
        }
        self.snapshots.append(snapshot)
        return snapshot
    
    def report(self):
        """Generate a report of all snapshots."""
        for snap in self.snapshots:
            print(f"{snap['label']}: active={snap['active']}, "
                  f"pending={snap['pending']}, unmatched={snap['unmatched']}, "
                  f"done={snap['done']}")

# Usage
monitor = MockMonitor()

pook.activate()
pook.enable_network()
monitor.snapshot("After activation")

# Setup mocks
pook.get('https://api.example.com/step1').reply(200).json({'step': 1})
pook.get('https://api.example.com/step2').reply(200).json({'step': 2})
monitor.snapshot("After mock setup")

# Execute step 1
requests.get('https://api.example.com/step1')
monitor.snapshot("After step 1")

# Execute step 2  
requests.get('https://api.example.com/step2')
monitor.snapshot("After step 2")

# Make unexpected request
requests.get('https://api.example.com/unexpected')
monitor.snapshot("After unexpected request")

monitor.report()
pook.off()

# Output:
# After activation: active=True, pending=0, unmatched=0, done=True
# After mock setup: active=True, pending=2, unmatched=0, done=False
# After step 1: active=True, pending=1, unmatched=0, done=False
# After step 2: active=True, pending=0, unmatched=0, done=True
# After unexpected request: active=True, pending=0, unmatched=1, done=True

Detailed Mock Inspection

import pook
import requests

def inspect_mock_details():
    """Detailed inspection of individual mock states."""
    
    pook.activate()
    
    # Create mocks with different configurations
    mock1 = pook.get('https://api.example.com/limited').times(2).reply(200)
    mock2 = pook.get('https://api.example.com/persistent').persist().reply(200)
    mock3 = pook.get('https://api.example.com/unused').reply(200)
    
    def print_mock_state(mock, name):
        print(f"{name}:")
        print(f"  Done: {mock.isdone()}")
        print(f"  Matched: {mock.ismatched()}")
        print(f"  Total matches: {mock.total_matches}")
        print(f"  Calls: {mock.calls}")
    
    print("Initial state:")
    print_mock_state(mock1, "Limited mock (2 times)")
    print_mock_state(mock2, "Persistent mock")
    print_mock_state(mock3, "Unused mock")
    
    # Make some requests
    requests.get('https://api.example.com/limited')      # First hit
    requests.get('https://api.example.com/persistent')   # First hit
    
    print("\nAfter first requests:")
    print_mock_state(mock1, "Limited mock")
    print_mock_state(mock2, "Persistent mock") 
    print_mock_state(mock3, "Unused mock")
    
    requests.get('https://api.example.com/limited')      # Second hit (expires)
    requests.get('https://api.example.com/persistent')   # Second hit (continues)
    
    print("\nAfter second requests:")
    print_mock_state(mock1, "Limited mock")
    print_mock_state(mock2, "Persistent mock")
    print_mock_state(mock3, "Unused mock")
    
    print(f"\nOverall state:")
    print(f"  Total pending: {pook.pending()}")
    print(f"  All done: {pook.isdone()}")
    
    # List remaining pending mocks
    pending = pook.pending_mocks()
    if pending:
        print(f"  Pending mock URLs: {[m.request.rawurl for m in pending]}")
    
    pook.off()

Error Debugging Helpers

import pook
import requests

def debug_mock_matching():
    """Helper for debugging why requests aren't matching mocks."""
    
    pook.activate()
    
    # Setup a mock with specific criteria
    mock = pook.post('https://api.example.com/users') \
        .header('Content-Type', 'application/json') \
        .json({'name': 'John', 'age': 30}) \
        .reply(201)
    
    print(f"Mock registered. Pending: {pook.pending()}")
    
    # Make a request that might not match
    try:
        response = requests.post('https://api.example.com/users', 
                               json={'name': 'John', 'age': '30'},  # age as string
                               headers={'Content-Type': 'application/json'})
        print(f"Request succeeded: {response.status_code}")
    except Exception as e:
        print(f"Request failed: {e}")
    
    # Check what happened
    print(f"After request - Pending: {pook.pending()}")
    print(f"Mock matched: {mock.ismatched()}")
    
    if not mock.ismatched():
        print("Mock was not matched. Possible issues:")
        print("- Request body didn't match expected JSON structure")
        print("- Headers didn't match")
        print("- URL didn't match")
        print("- HTTP method didn't match")
    
    pook.off()

def validate_test_cleanup():
    """Ensure proper test cleanup and isolation."""
    
    def run_test():
        pook.activate()
        pook.get('https://api.example.com/test').reply(200)
        # Simulate test that forgets cleanup
        # pook.off()  # Commented out - simulates forgotten cleanup
    
    # Before test
    print(f"Before test - Active: {pook.isactive()}, Pending: {pook.pending()}")
    
    run_test()
    
    # After test
    print(f"After test - Active: {pook.isactive()}, Pending: {pook.pending()}")
    
    if pook.isactive() or pook.pending() > 0:
        print("WARNING: Test didn't clean up properly!")
        print("This could affect other tests.")
        pook.off()  # Force cleanup
    
    print(f"After cleanup - Active: {pook.isactive()}, Pending: {pook.pending()}")

MatcherEngine Inspection

class MatcherEngine:
    """
    HTTP request matcher engine used by Mock to test if an intercepted 
    outgoing HTTP request should be mocked out.
    """
    
    def match(self, request):
        """
        Matches HTTP request against registered matchers.
        
        Parameters:
        - request: HTTP request to match
        
        Returns:
        tuple: (bool, list[str]) - Match success and error messages
        """

Usage examples:

import pook

# Access matcher engine for detailed debugging
mock = pook.get('https://api.example.com/test')
mock.json({'required': 'field'}).reply(200)

# Simulate a request for testing
from pook import Request
test_request = Request(
    method='GET',
    url='https://api.example.com/test',
    json={'different': 'field'}
)

# Test if request would match
success, errors = mock._matchers.match(test_request)
print(f"Would match: {success}")
if errors:
    print(f"Match errors: {errors}")

Install with Tessl CLI

npx tessl i tessl/pypi-pook

docs

engine-management.md

index.md

mock-configuration.md

mock-creation.md

state-inspection.md

tile.json