I'm trying to assert the input of a patched request.get() call which is wrapped inside another function.
def get_data(*args):
# logic to define url, based on '*args'
url = 'some_url?arg1&arg3'
# call I want to patch and assert the url of
response = request.get(url)
# process response
stuff = 'processed_response'
return stuff
test script:
def mock_response_200(url):
response = mock.MagicMock()
response.status_code = 200
response.json = mock.Mock(return_value={
0: {'key1': 'value1', 'key2': 'value2'}
})
return response
@mock.patch('request.get', new=mock_response_200)
def test_get_data():
arg1 = 'arg1'
arg2 = None
arg3 = 'arg3'
stuff = get_data(arg1, arg2, arg3)
# <assert input arguments of patched function here>
How do I assert the url which was passed to mocked_response_200? mocked_response_200 isn't 'known' within test_get_data.
I've checked other posts on here This one comes close but the answer uses a different patch method. Any help would be greatly appreciated.
You are patching an unknown get
object in the request
module. You probably don't have such a module or object.
You need to address the request
object in the module under test . If get_data
lives in the module views
, then you need to patch views.request.get
here:
@mock.patch('views.request.get', new=mock_response_200)
From the mock.patch()
documentation :
target should be a string in the form
'package.module.ClassName'
. The target is imported and the specified object replaced with the new object, so the target must be importable from the environment you are callingpatch()
from. The target is imported when the decorated function is executed, not at decoration time.
I'd not use a function here at all however. Just let mock patch the get()
method for you, then configure that mock object. You can of course delegate that to a helper function:
def config_response_200_mock(request_get):
response = request_get.return_value
response.status_code = 200
response.json.return_value = {
0: {'key1': 'value1', 'key2': 'value2'}
}
return response
@mock.patch('views.request.get')
def test_get_data(request_get):
response_mock = config_response_200_mock(request_get)
arg1 = 'arg1'
arg2 = None
arg3 = 'arg3'
stuff = get_data(arg1, arg2, arg3)
You can also create a magic mock object in such a function, then pass in the function to new_callable
on mock.patch()
:
def response_200_mock():
get_mock = mock.MagicMock()
response = get_mock.return_value
response.status_code = 200
response.json.return_value = {
0: {'key1': 'value1', 'key2': 'value2'}
}
return get_mock
@mock.patch('views.request.get', new_callable=response_200_mock)
def test_get_data(request_get):
arg1 = 'arg1'
arg2 = None
arg3 = 'arg3'
stuff = get_data(arg1, arg2, arg3)
Either way, the object used to patch request.get
with is passed into test_get_data
as an argument.
Demo of either approach (using patch
as a context manager instead of a decorator, but the principle is the same:
>>> def config_response_200_mock(request_get):
... response = request_get.return_value
... response.status_code = 200
... response.json.return_value = {
... 0: {'key1': 'value1', 'key2': 'value2'}
... }
... return response
...
>>> with mock.patch('__main__.request.get') as request_get:
... response_mock = config_response_200_mock(request_get)
... arg1 = 'arg1'
... arg2 = None
... arg3 = 'arg3'
... stuff = get_data(arg1, arg2, arg3)
...
>>> stuff
'processed_response'
>>> response_mock.json()
{0: {'key1': 'value1', 'key2': 'value2'}}
>>> request_get.mock_calls
[call('some_url?arg1&arg3')]
>>> def response_200_mock():
... get_mock = mock.MagicMock()
... response = get_mock.return_value
... response.status_code = 200
... response.json.return_value = {
... 0: {'key1': 'value1', 'key2': 'value2'}
... }
... return get_mock
...
>>> with mock.patch('__main__.request.get', new_callable=response_200_mock) as request_get:
... arg1 = 'arg1'
... arg2 = None
... arg3 = 'arg3'
... stuff = get_data(arg1, arg2, arg3)
...
>>> stuff
'processed_response'
>>> request_get.return_value.json()
{0: {'key1': 'value1', 'key2': 'value2'}}
>>> request_get.mock_calls
[call('some_url?arg1&arg3')]
Unittest is to test one basic component at a time. So every other component called within needs to be tested in other tests.
If you only want to assert url
was passed properly then I would suggest not to use new
keyword
@mock.patch('module.process_response')
@mock.patch('module.request.get')
def test_get_data(mock_get, mock_process_response):
arg1 = 'arg1'
arg2 = None
arg3 = 'arg3'
url = "what ever url"
stuff = get_data(arg1, arg2, arg3)
mock_get.assert_called_with(url)
This will help you to identify if it was called properly. In the later tests use the new
keyword to check if the response returned was processed_properly.
The mock_process_response
is a MagicMock
object and will prevent process_response
from being called and hence wont require the mock_get
to define json
or status_code
to be defined.
edits: added mock for process_response
.
First of all, many thanks to @Ja8zyjits and @Martijn Pieters for their help.
The solution which worked for me looks as follows:
@mock.patch('request.get', side_effect=mock_response_200)
def test_get_data(mock_get):
arg1 = 'arg1'
arg2 = None
arg3 = 'arg3'
expected_url = 'some_url?arg1&arg3'
stuff = get_data(arg1, arg2, arg3)
mock_get.assert_called_with(url)
I can't say I fully understand the interaction between passing mock_response_200
as 'side_effect'
and passing mock_get
to test_get_data
yet. But using this combination I was able to both assert the input of the patched request.get and return the desired response to prevent raising any errors during processing of the response.
edit: code formatting
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.