How to mock an object returned by a mocked object?
Question: I don't quite have the hang of Python mocks.

Clearly I don't want my test code to call the actual method`requests.post()`
in the following method, so I want to to mock its behavior instead:

    
    
    def try_post(self, url, body):
        r = requests.post(url, data=body)
        msg = str(r.status_code) + " " + r.content + "\n"
        if r.status_code >= 300:
            sys.stderr.write("Error: POST returned " + msg)
    

My question: How do I mock the object returned by `requests.post()`, i.e. the
response object?

For example, I'd like to write one test where r.status_code is 200 and another
where r.status_code is 300 so I can test the conditional logic. Also, I'd need
to mock r.content to return some string.

My non-working code follows:

    
    
    from monitor_writer import MonitorWriter
    import mock
    import unittest
    
    class TestMonitorWriter(unittest.TestCase): 
    
        @mock.patch('monitor_writer.requests')
        def test_conforming_write(self, mock_requests):
            xml_frag = """
    <InstantaneousDemand>
    </InstantaneousDemand>
    """
            mock_requests.status_code.return_value = 200
            mock_requests.content.return_value = "OK"
    
            writer = MonitorWriter()
            writer.update(xml_frag)
            self.assertTrue(mock_requests.post.called, "Failed to call requests.post")
    

This test fails with `TypeError: expected a character buffer object` because
r.status_code and r.content evaluate to mock.MagicMock objects, not strings,
and the try_post() method is attempting to catenate them.


Answer: You want to mock `requests.post` directly, rather than the entire `requests`
module:

    
    
    class TestMonitorWriter(unittest.TestCase): 
    
        @mock.patch('monitor_writer.requests.post')
        def test_conforming_write(self, mock_post):
            xml_frag = """
    <InstantaneousDemand>
    </InstantaneousDemand>
    """
            response = mock.MagicMock()
            response.status_code = 200
            respone.content = "OK"
            mock_post.return_value = response
    
            writer = MonitorWriter()
            writer.update(xml_frag)
            self.assertTrue(mock_post.called, "Failed to call requests.post")
    

Once we've mocked the function you're actually calling, we create a mock
response object, set `status_code` and `content` on the mocked response to the
desired values, and finally assign the mocked response to the `return_value`
of our mocked `post` function.

