2

I'm trying to mock out urllib.request.urlopen's Read method on Python 3:

Function Code:

try:
    with request.urlopen(_webhook_url, json.dumps(_message).encode('utf-8')) as _response:
        _response_body = _response.read()
        return _response_body

Test Code:

with mock.patch('urllib.request.urlopen') as mock_urlopen:
    response_mock = MagicMock()
    response_mock.read.return_value = 'ok'
    mock_urlopen.return_value = response_mock


    with self.stubber:
        _response = NotifySlack.lambda_handler(_event)

        self.assertEqual('ok', _response)

If I call response_mock.read() I get the 'ok' value returned, however when I assert the return value I get a mock signature:

Expected :ok
Actual   :<MagicMock name='urlopen().__enter__().read()' id='2148156925992'>

Any ideas on why the mock isn't returning the value assigned to read()?

alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
Colin Bowern
  • 2,152
  • 1
  • 20
  • 35

1 Answers1

4

To follow @jonrsharpe's comment and the Python: Mocking a context manager thread, to properly mock the context manager in this case, you would need this interestingly looking line:

mock_urlopen.return_value.__enter__.return_value.read.return_value = 'ok'
#^^^^^^context manager to return response^^^^^^^|^^^read method^^^
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
  • That works, thanks! For other Python newbies (myself included), this particular approach seems to be required because I used the with keyword (which creates the context manager construct referred to in the answer). Had the been "_response = request.urlopen..." the original code would appear to have worked. – Colin Bowern Jan 05 '18 at 12:34
  • 1
    Also mentioned in the `unittest.mock` docs: https://docs.python.org/3/library/unittest.mock.html#mocking-magic-methods – jonrsharpe Jan 05 '18 at 13:13
  • You can also do `mock_urlopen().__enter__().read.return_value = 'ok'`, which is shorter. If you are tracking whether your mock has been called then you can reset the call count with `mock_urlopen.reset_mock()`. – Craig Blaszczyk Aug 21 '18 at 09:18