How to override Django view with mock.patch for testing?

Viewed 1905

Is it possible to override functions behaviour in class with mock?

This is for python 3.6.8, django 2.2.2

views.py:


YEAR_PATTERN = r"\(\d{4}\)\s*$"
LOCKED_FROM_EXTERNAL_API = False


class FetchFromExternalApi(APIView):

    @staticmethod
    def fetch_from_url(source):
        return urlopen('http://files.grouplens.org/datasets/movielens/%s.zip' % source, timeout=1)

    def post(self, request):
        global LOCKED_FROM_EXTERNAL_API, YEAR_PATTERN
        if LOCKED_FROM_EXTERNAL_API is False:
            LOCKED_FROM_EXTERNAL_API = True
            try:
                source = request.data['source']
            except KeyError:
                LOCKED_FROM_EXTERNAL_API = False
                return Response('no source data in body',
                                status=status.HTTP_400_BAD_REQUEST)
            if source in settings.AVAILABLE_SOURCES:
                try:
                    response = self.fetch_from_url(request.data['source'])
                except URLError:
                    LOCKED_FROM_EXTERNAL_API = False
                    return Response("External server respond time out",
                                    status=status.HTTP_504_GATEWAY_TIMEOUT)

I would like to write test that will override behaviour of fetch_from_url method, and fully simulate it.

1 Answers

Yes, you can patch class attributes and methods.

In your case, you'll also need to return a fake http response object with expected data from the patched object.

Example:

class FakeResponse:
    """A class for creating fake http responses for the patched method"""
    def __init__(self, body, status):
        self.body = body
        self.status = status



class MyTest(TestCase):
    def test_something(self):
        with mock.patch('file_name.FetchFromExternalApi.fetch_from_url') as mock_fetch:
            # now the `mock_fetch` will act as a proxy object for the 
            # actual fetch_from_url method

            # set the return value on `mock_fetch` object as expected 
            # from the `fetch_from_url` method

            fake_response = FakeResponse(body="Some expected data body", status=200)

            mock_fetch.return_value = fake_response

            # now you can perform tests against the returned data
            self.assertTrue(whatever)
Related