requests-mock Documentation

repository·master·Indexed 19 days ago

https://github.com/jamielennox/requests-mock

A Python library for stubbing out HTTP requests made via the 'requests' library. It allows developers to simulate network responses in tests without making actual network calls using tools like the Mocker class, transport adapters via requests_mock.Adapter, and optional fixture support for test cases.

Tokens
8.4K
Snippets
35
Records
40
Agent score
65%

What's inside requests-mock

  1. Introduction to requests-mock

    master
    requests-mock is a library designed to stub out the HTTP requests portions of your testing code. It works by providing a custom adapter that intercepts calls to specific URIs and returns predefined responses instead of making actual network requests.
  2. How nested mockers work

    master

    When nesting mockers, the innermost Mocker takes precedence and replaces all others.

    If real_http=True is set in the innermost Mocker, and a request is not handled by it, the request is passed to the containing (outer) Mocker. This chain continues until a Mocker handles the request, a NoMockAddress exception is raised, or the request is passed to the unmocked requests.Session (if real_http=True is set all the way up).

    Warning: When manually starting/stopping mockers, always stop the innermost mocker first to avoid undefined behavior.

    import requests
    import requests_mock
    
    url = "https://www.example.com/"
    with requests_mock.Mocker() as outer_mock:
        outer_mock.get(url, text='outer')
        with requests_mock.Mocker(real_http=True) as middle_mock:
            with requests_mock.Mocker() as inner_mock:
                inner_mock.get(url, real_http=True)
                # The request flows through the hierarchy
  3. Create dynamic responses using callbacks

    master

    Instead of a static body, you can provide a callback function to register_uri. This allows you to modify the response dynamically based on the incoming request or internal logic.

    Callback Signature

    A callback must be a function with the following signature:

    def callback(request, context):
        ...

    Arguments

    • request: The requests.Request object used for the call.
    • context: An object containing collected data about the response. You can modify the following properties on context to change the final response:
      • context.status_code: The status code to return.
      • context.reason: The HTTP status reason string.
      • context.headers: A dictionary of headers to return.
      • context.cookies: A requests_mock.CookieJar to be merged into the response.

    The callback should return a value suitable for the body element type specified (e.g., a string if text was used, or a dict if json was used).

    Note for raw responses: If you use a callback for the raw attribute, it must return an HTTPResponse. Ensure the HTTPResponse has preload_content=False to function correctly.

    def text_callback(request, context):
        context.status_code = 200
        context.headers['Test1'] = 'value1'
        return 'response'
    
    adapter.register_uri('GET', 'mock://test.com/3', text=text_callback, headers={'Test2': 'value2'}, status_code=400)
    
    # The resulting response will have status 200, headers {'Test1': 'value1', 'Test2': 'value2'}, and text 'response'
  4. How requests-mock works via custom adapters

    master

    At a low level, requests-mock provides a custom Adapter that can be mounted onto a requests.Session. This allows you to define specific URI patterns and their corresponding responses. When a request matches a registered URI, the adapter returns the mocked response.

    import requests
    import requests_mock
    
    session = requests.Session()
    adapter = requests_mock.Adapter()
    session.mount('mock://', adapter)
    
    # Register a URI with a specific method and response text
    adapter.register_uri('GET', 'mock://test.com', text='data')
    
    resp = session.get('mock://test.com')
    print(resp.status_code, resp.text)
    # (200, 'data')
  5. Use additional_matcher for dynamic matching

    master

    The additional_matcher argument allows you to pass a callback function for dynamic matching logic. This function receives the request object as a parameter and must return True if the request matches, or False otherwise.

    This is useful for inspecting request bodies (like JSON or XML) or other complex logic that standard matchers don't support.

    def match_request_text(request):
        # request.text may be None; handle safely
        return 'hello' in (request.text or '')
    
    adapter.register_uri('POST', 'mock://test.com/additional', additional_matcher=match_request_text, text='resp')
    def match_request_text(request):
        return 'hello' in (request.text or '')
    
    adapter.register_uri('POST', 'mock://test.com/additional', additional_matcher=match_request_text, text='resp')
  6. Mocking specific requests.Session instances

    master

    By default, requests_mock intercepts calls made via the global requests module. However, you can use the session parameter in requests_mock.Mocker to target a specific requests.Session instance. This ensures that the mocking only applies to that session and does not affect global requests or other sessions.

    import requests
    import requests_mock
    
    url = "https://www.example.com/"
    session = requests.Session()
    
    # Only 'session' will be affected by this mocker
    with requests_mock.Mocker(session=session) as session_mock:
        session_mock.get(url, text='session')
        print(session.get(url).text)  # Output: 'session'
        print(requests.get(url).text) # Output: (whatever global mock/real request is)
  7. Match requests by headers

    master

    You can provide a dictionary to the request_headers argument. The mock will only match if the incoming request contains at least all the headers specified in your dictionary. Additional headers in the request are ignored.

    # Matches if 'key': 'val' is present, regardless of other headers
    adapter.register_uri('POST', 'mock://test.com/headers', request_headers={'key': 'val'}, text='resp')
    adapter.register_uri('POST', 'mock://test.com/headers', request_headers={'key': 'val'}, text='resp')
  8. Use requests-mock with pytest fixtures

    master

    When using pytest, you do not need to import requests-mock or use the @requests_mock.Mocker decorator. Instead, requests-mock provides a built-in pytest fixture named requests_mock. You can use it by simply adding requests_mock as a parameter to your test function. This fixture provides the same interface as the requests_mock.Mocker class.

    Note: If you are using pytest fixtures, avoid using the @requests_mock.Mocker decorator syntax found in some documentation examples, as it can conflict with how pytest handles function arguments.

    import pytest
    import requests
    
    def test_url(requests_mock):
        requests_mock.get('http://test.com', text='data')
        assert 'data' == requests.get('http://test.com').text
  9. Simulate connection errors by raising exceptions

    master

    To emulate network-level issues like connection timeouts or SSL errors, use the exc parameter in register_uri instead of a body parameter. This will cause requests to raise the specified exception when the mock is hit.

    import requests
    
    # Emulate a connection timeout
    adapter.register_uri('GET', 'mock://test.com/6', exc=requests.exceptions.ConnectTimeout)
    
    # session.get('mock://test.com/6') will now raise requests.exceptions.ConnectTimeout