'How to mock a (PyActiveResource) pyactiveresource.connection UnauthorizedAccess response return, using Django TestCase?

I need to create a unit test that mock a REST API failure call, with a side effect of returning an UnauthorizedAccess exception, from the PyActiveResource project (https://github.com/Shopify/pyactiveresource) and store it in the DB. What I've create so far worked and I've got the desired returned side effect. Then, I catch it on the function foo.function_that_call_myfuncion() which looks like this my_func.py:

from pyactiveresource.connection import UnauthorizedAccess

class MyFuncAnyThing:
    ...
    def function_that_call_myfuncion(self, attr=None):
        try:
            module.MyTestClass.myfunction(attr)
        except UnauthorizedAccess as error:
            #Catch the error response object and store it on DB 
            resp_body = error.response.body
            resp_code = error.response.code
            #store it on DB
    ...

And my test file looks like this unit_test.py:

from pyactiveresource.connection import UnauthorizedAccess

class TestFoo(TestCase):
    def test_exception_unauthorized_access(self):
        foo = SomeThingFactory()
        
        with patch('module.MyTestClass.myfunction', side_effect=UnauthorizedAccess()):
            foo.function_that_call_myfuncion()
        
        #assertions goes below here
        ...

So, when the execution reached the try block on function_that_call_myfuncion from my_func.py module, the mock function return the desired exception (UnauthorizedAccess) and the object returned looks like this:

error
UnauthorizedAccess('Response(code=None, body="", headers={}, msg="")')

My problems begins when I try to mock the Response body returned on the UnauthorizedAccess exception. This is what I'm doing: unit_test.py:

from pyactiveresource.connection import UnauthorizedAccess

class TestFoo(TestCase):
    def test_exception_unauthorized_access(self):
        foo = SomeThingFactory()
        bar = MagicMock()
        bar.code = 401
        bar.body = '{"errors": "Login or wrong password"}'

        with patch('module.MyTestClass.myfunction', side_effect=UnauthorizedAccess(bar)):
            foo.function_that_call_myfuncion()
        
        #assertions goes below here
        ...

And that's is how the mocked object looks like:

error
UnauthorizedAccess('Response(code=401, body="<MagicMock name=\'mock.read()\' id=\'2243840764512\'>", headers={}, msg="<MagicMock name=\'mock.msg\' id=\'2243840808464\'>")')

Note that the code attribute on Response is 401, but the body is empty, even though I've set it here bar.body = '{"errors": "Login or wrong password"}'. I also tried to create a Response object and pass it as parameter on the constructor for UnauthorizedAccess class, which is a subclass of class ConnectionError(Error): of the pyactiveresource.connection lib code (https://github.com/Shopify/pyactiveresource/blob/e609d844ebace603f74bc5f0a67e9eafe7fb25e1/pyactiveresource/connection.py#L34)

unit_test.py:

from pyactiveresource.connection import UnauthorizedAccess, Response

class TestFoo(TestCase):
    def test_exception_unauthorized_access(self):
        foo = SomeThingFactory()
        resp = Response(code=401,body='{"errors": "Login or wrong password"}')

        with patch('module.MyTestClass.myfunction',  side_effect=UnauthorizedAccess(response=resp)):
            foo.function_that_call_myfuncion()
        
        #assertions goes below here
        ...

But then I got this error from the Class Response:

@classmethod
    def from_httpresponse(cls, response):
        """Create a Response object based on an httplib.HTTPResponse object.
    
        Args:
            response: An httplib.HTTPResponse object.
        Returns:
            A Response object.
        """
>       return cls(response.code, response.read(),
                   dict(response.headers), response.msg, response)
E       AttributeError: 'Response' object has no attribute 'read'

What am I missing? I just couldn't figure out how to set the 'read' attribute on the constructor, so that I can get the body value.

I'm using Python 3.8, Django 2.2



Solution 1:[1]

I managed to mock Shopify's ClientError exceptions by doing something along the lines of:

import urllib.error
from io import BytesIO
import pyactiveresource.testing.http_fake
pyactiveresource.testing.http_fake.initialize()
response = urllib.error.HTTPError('', 401, '', {}, BytesIO(b''))
pyactiveresource.testing.http_fake.TestHandler.set_response(response)

Which I learned about by digging into the Shopify/pyactiveresource tests.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dharman