Home > Net >  How to mock a method of a dependent class?
How to mock a method of a dependent class?

Time:03-15

I'm trying to test a method (Creator.do_a_call) which calls another class's method (Requester.make_request) which makes external calls (all the code is below). So I want to mock Request.make_request to return a mocked out response object so it doesn't do any external calls, but when I do that it gives me an error (see below)

Here's the code:

Requester.py:

from requests import Response, Session, Request
class Requester:
    url: str = ''

    def __init__(self, url: str):
        self.url = url

    def make_request(self, path: str, method: str = 'get', body: dict = None, custom_headers: dict = None) -> Response:
        print("make_request")
        url = self.url   path
        session = Session()
        request = Request(method, url, json=body)
        response = session.send(request.prepare())
        return response

Creator.py

from Requester import Requester
from http import HTTPStatus as status
from requests import Response

class Creator:
    def do_a_call(self, url) -> Response:
        print("do_a_call")
        requester = Requester(url)
        response = requester.make_request(
                    "/",
                    "GET"
                )
        print(type(response))
        if response.status_code != status.OK and response.status_code != status.ACCEPTED:
            raise Exception(response.status_code, response.text)
        return response

main.py

from Creator import Creator

print("main")
creator = Creator()
response = creator.do_a_call("https://google.com")
print(response)

test.py

import unittest
from unittest.mock import patch
from Creator import Creator
from requests import Session
import requests_mock
class CreatorTest(unittest.TestCase):

    def mocked_make_request(self, two, three):
        session = Session()
        mocker = requests_mock.Mocker(session=session)
        adapter = requests_mock.Adapter()
        adapter.register_uri('POST', 'mock://test.com', text="Invalid policy number supplied", status_code=400)
        return mocker.post("mock://test.com", status_code=400)

    @patch('Requester.Requester.make_request')
    def test_do_a_call_side_effect(self, make_request):
        creator = Creator()
        make_request.side_effect = self.mocked_make_request
        response = creator.do_a_call("mock://test.com")
        self.assertEqual(200, response.status_code)

    @patch('Requester.Requester.make_request')
    def test_do_a_call_return_value(self, make_request):
        creator = Creator()
        make_request.return_value = self.mocked_make_request("", "")
        response = creator.do_a_call("mock://test.com")
        self.assertEqual(200, response.status_code)

        
if __name__ == "__main__":
    unittest.main()

In my research I've found two ways that should work - changing the return_value of the mock, or changing the side_effect of the mock, both of which give me the same error:

Traceback (most recent call last):
  File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/unittest/mock.py", line 1325, in patched
    return func(*newargs, **newkeywargs)
  File "test.py", line 19, in test_do_a_call_side_effect
    response = creator.do_a_call("mock://test.com")
  File "/Users/citrja/Documents/git/python_test/Creator.py", line 14, in do_a_call
    if response.status_code != status.OK and response.status_code != status.ACCEPTED:
AttributeError: '_Matcher' object has no attribute 'status_code'

CodePudding user response:

I've found out what was wrong! I Was not attaching the adapter to the session, I had thought this happened automagically but apparently not. Here's the updated mocked_make_request function:

def mocked_make_request(self, two, three):
        session = Session()
        mocker = requests_mock.Mocker(session=session)
        adapter = requests_mock.Adapter()
        adapter.register_uri('GET', 'mock://test.com', text="Invalid policy number supplied", status_code=400)
        session.mount("mock://", adapter)
        return session.get("mock://test.com")
  • Related