real_http gets overwritten in a mocked callback

Bug #1639039 reported by Kaleb Smart
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
requests-mock
Triaged
Medium
Unassigned

Bug Description

I wrote a component test in which the mocked API does a little more than just returning HTTP responses: later, the API will send an HTTP request to a listener started by the module being tested. In order to do this, I call a function which will send the request back to the component before completing its response. I get a NoMockAddress exception when I attempt to make this request from inside the callback, yet I have validated that I can send an unmatched request outside of the callback context.

Here is code that does this:

import module
import requests
import requests_mock

def callback(request, context):
    send_another_request()

def send_another_request():
    requests.get('http://httpbin.org/get')

def test_foo():
    with requests_mock.Mocker(real_http=True) as mock:
        mock.post('http://foo.org', text=callback)
        module.foo()

Tags: realhttp
Revision history for this message
Jamie Lennox (jamielennox) wrote :

Interesting, I had never considered this case.

What's happening is the mocking is there are two levels of mocking, mocking send() when you call Mocker and mocking get_adapter only for the period of that fake send() call. If you get NoMockAdress with real_http the send process unmocks the get_adapter call and then re-issues the send with the real adapter.

Because callback is being invoked from the mocked adapter calling the inner requests.get send() is then mocking get_adapter again. This means that when the inner function tries to do a real_http it is restoring the outer get_adapter function and re-issuing the call to another mocked adapter.

I'll have a look and see if there's a way we can do some sort of depth tracking or something to solve this case.

Changed in requests-mock:
status: New → Triaged
importance: Undecided → Medium
Revision history for this message
Jamie Lennox (jamielennox) wrote :

Actually it raises an interesitng question of use. Given:

def callback(request, context):
    resp = requests.get('http://example.com')
    context.status_code = resp.status_code
    return resp.text

with requests_mock.Mocker(real_http=True) as mock:
    mock.post('http://foo.org', text=callback)
    mock.get('http://example.com', text='abc')

    print requests.post('http://foo.org')

would we expect the request made in callback() to trigger the mock created at the higher level? I guess so, but it's an odd case.

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.