CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-responses

A utility library for mocking out the requests Python library

Pending
Overview
Eval results
Files

registries.mddocs/

Response Registries

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.

Capabilities

FirstMatchRegistry

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"] == 1

OrderedRegistry

A 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 fail

Registry Usage Patterns

Simulating API Sequences

OrderedRegistry 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 True

Simulating Different Response Patterns

FirstMatchRegistry 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()

Registry Selection and Switching

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"})

Custom Registry Implementation

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)

Registry Error Handling

OrderedRegistry Errors

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_message

Registry State Management

def 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()) == 0

Install with Tessl CLI

npx tessl i tessl/pypi-responses

docs

basic-mocking.md

index.md

matchers.md

recording.md

registries.md

requests-mock.md

response-types.md

tile.json