Python 如何在另一个函数中模拟一个函数
Python how to mock a function within another function
我对破坏单元测试的功能进行了一些更改。以前我有一个 common.py 包含函数 request_url:
import requests
def request_url(method, url):
return _request_url(method, url)
def _request_url(method, url):
return requests.request(method, url)
这是测试:
@mock.patch("requests.request", autospec=True)
def test_request_url_200(self, mock_request):
response = mock.MagicMock()
response.status_code = 200
method = mock.sentinel.method
path = "url"
result = common.request_url(
method,
path
)
self.assertListEqual([
mock.call(
mock.sentinel.method,
"url"
),
], list(mock_request.mock_calls))
self.assertListEqual([mock.call.raise_for_status()], list(response.mock_calls))
self.assertEqual(mock_request.return_value, result)
在功能发生变化后,我不是简单地调用 requests.request,而是先启动一个会话,安装一个 TLS 适配器,然后像这样调用会话的请求函数:
def _request_url(method, url):
session = requests.session()
adapter = TlsAdapter(ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1)
session.mount("https://", adapter)
return response = session.request(method, url)
在这里,我不确定如何准确地模拟 session.request,因为它可以通过会话变量获得。我尝试修补 requests.session.request 但这在这里不起作用。
有谁知道如何模拟这个函数?
谢谢!
我觉得原因在@mock.patch(...)
。您设置了 autospec=True
,但是您的 mock_request
没有 return 数据(在您的情况下每次都是 Mock
)。 The documentation 说:
If you set autospec=True then the mock will be created with a spec
from the object being replaced. All attributes of the mock will also
have the spec of the corresponding attribute of the object being
replaced. ...
尝试调用:
print(mock_request.return_value) # <MagicMock name='request()()' id='4343989392'>
# or
# print(mock_request.return_value.return_value) # <MagicMock name='request()()()' id='4344191568'>
如您所见,requests.request
是 'mocked' 但 return 没有任何数据。您可以使用 return_value
或 side_effect
设置预期数据。举个例子:
from unittest import TestCase
import mock
from common import request_url
class MyTestExample(TestCase):
def test_request_url_1(self):
mocked_request = mock.patch('requests.request', side_effect=['one', 'two', 'tree', ])
mocked_request.start()
# request_url(...) will return item from list
self.assertEqual('one', request_url('test', 'url'))
self.assertEqual('two', request_url('test', 'url'))
self.assertEqual('tree', request_url('test', 'url'))
mocked_request.stop()
def test_request_url_2(self):
result = {'my': 'dict'}
mocked_request = mock.patch('requests.request', return_value=result)
mocked_request.start()
# request_url(...) will return static data
self.assertEqual(result, request_url('test', 'url'))
self.assertEqual(result, request_url('test', 'url'))
self.assertEqual(result, request_url('test', 'url'))
mocked_request.stop()
因此,您只需描述预期数据即可。您也可以 mock
API 使用 httpretty.
希望这对您有所帮助。
我对破坏单元测试的功能进行了一些更改。以前我有一个 common.py 包含函数 request_url:
import requests
def request_url(method, url):
return _request_url(method, url)
def _request_url(method, url):
return requests.request(method, url)
这是测试:
@mock.patch("requests.request", autospec=True)
def test_request_url_200(self, mock_request):
response = mock.MagicMock()
response.status_code = 200
method = mock.sentinel.method
path = "url"
result = common.request_url(
method,
path
)
self.assertListEqual([
mock.call(
mock.sentinel.method,
"url"
),
], list(mock_request.mock_calls))
self.assertListEqual([mock.call.raise_for_status()], list(response.mock_calls))
self.assertEqual(mock_request.return_value, result)
在功能发生变化后,我不是简单地调用 requests.request,而是先启动一个会话,安装一个 TLS 适配器,然后像这样调用会话的请求函数:
def _request_url(method, url):
session = requests.session()
adapter = TlsAdapter(ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1)
session.mount("https://", adapter)
return response = session.request(method, url)
在这里,我不确定如何准确地模拟 session.request,因为它可以通过会话变量获得。我尝试修补 requests.session.request 但这在这里不起作用。
有谁知道如何模拟这个函数?
谢谢!
我觉得原因在@mock.patch(...)
。您设置了 autospec=True
,但是您的 mock_request
没有 return 数据(在您的情况下每次都是 Mock
)。 The documentation 说:
If you set autospec=True then the mock will be created with a spec from the object being replaced. All attributes of the mock will also have the spec of the corresponding attribute of the object being replaced. ...
尝试调用:
print(mock_request.return_value) # <MagicMock name='request()()' id='4343989392'>
# or
# print(mock_request.return_value.return_value) # <MagicMock name='request()()()' id='4344191568'>
如您所见,requests.request
是 'mocked' 但 return 没有任何数据。您可以使用 return_value
或 side_effect
设置预期数据。举个例子:
from unittest import TestCase
import mock
from common import request_url
class MyTestExample(TestCase):
def test_request_url_1(self):
mocked_request = mock.patch('requests.request', side_effect=['one', 'two', 'tree', ])
mocked_request.start()
# request_url(...) will return item from list
self.assertEqual('one', request_url('test', 'url'))
self.assertEqual('two', request_url('test', 'url'))
self.assertEqual('tree', request_url('test', 'url'))
mocked_request.stop()
def test_request_url_2(self):
result = {'my': 'dict'}
mocked_request = mock.patch('requests.request', return_value=result)
mocked_request.start()
# request_url(...) will return static data
self.assertEqual(result, request_url('test', 'url'))
self.assertEqual(result, request_url('test', 'url'))
self.assertEqual(result, request_url('test', 'url'))
mocked_request.stop()
因此,您只需描述预期数据即可。您也可以 mock
API 使用 httpretty.
希望这对您有所帮助。