Search code examples
pythonmockingpython-mock

Python mock access "real" objects while mocking a module


Is it possible to access "real" objects while mocking a module? What I'm trying to do is mock some function, but throw "real" exceptions, like this:

@mock.patch('my_project.requests')
def test_foo(self,  mock_requests):

    mock_requests.post = mock.Mock(side_effect=requests.ConnectionError())

    thread = CommandThread("command", 3, 2, 0)
    thread.run() #this is were I exercise requests.post

    self.assert(thread.was_successful is False)

Inside my CommandThread I have a check like

try:
    requests.post(url, data=data)
except (requests.ConnectionError, requests.Timeout):
    self.was_successful = False

however, my test fails because the exception is not caught inside the try/except block (when I do like except Exception: it works) The reason, I think, is simply because I mocked this "namespace" in my test case, so I actually raise my_project.requests.ConnectionError exception rather than proper, requests.ConnectionError from original package. Is it somehow possible to access/throw "real" exceptions?


Solution

  • This is happening because your mock is actually overwriting the entire requests module in your code. Here is how you can debug this:

    In your code, add this:

    try:
        requests.post('', data='')
    except (requests.ConnectionError, requests.Timeout):
        was_successful = False
    except Exception, err:
        import pdb
        pdb.set_trace()
    

    When you run the test, you will be dropped into the debugger so that you can take a look at what is happening. If we look at what you are catching, this is what we see:

    (Pdb) requests.ConnectionError
    <MagicMock name='requests.ConnectionError' id='4562438992'>
    

    You are actually catching a mock ConnectionError because your mock patched out the entire requests module and you were feeding in a real requests error.

    You can fix this by making your mock more specific and only overriding the post method on the requests module:

    @mock.patch('my_project.requests.post')
    def test_foo(self,  mock_requests):
        mock_requests.side_effect = requests.ConnectionError()
        ...