如何模拟依赖 class 的方法?
How to mock a method of a dependent class?
我正在尝试测试一个方法 (Creator.do_a_call),该方法调用另一个 class 的方法 (Requester.make_request),该方法进行外部调用(所有代码都在下面)。所以我想模拟 Request.make_request
到 return 一个模拟的 response
对象,这样它就不会进行任何外部调用,但是当我这样做时它会给我一个错误(见下文)
代码如下:
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()
在我的研究中,我发现 应该 工作的两种方式 - 更改模拟的 return_value,或更改模拟的 side_effect ,这两个都给我同样的错误:
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'
我发现问题了!我没有将适配器连接到会话,我以为这是自动发生的,但显然不是。这是更新后的 mocked_make_request
函数:
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")
我正在尝试测试一个方法 (Creator.do_a_call),该方法调用另一个 class 的方法 (Requester.make_request),该方法进行外部调用(所有代码都在下面)。所以我想模拟 Request.make_request
到 return 一个模拟的 response
对象,这样它就不会进行任何外部调用,但是当我这样做时它会给我一个错误(见下文)
代码如下:
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()
在我的研究中,我发现 应该 工作的两种方式 - 更改模拟的 return_value,或更改模拟的 side_effect ,这两个都给我同样的错误:
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'
我发现问题了!我没有将适配器连接到会话,我以为这是自动发生的,但显然不是。这是更新后的 mocked_make_request
函数:
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")