为了账号安全,请及时绑定邮箱和手机立即绑定

修补函数的 assert_call_with

修补函数的 assert_call_with

尚方宝剑之说 2021-12-09 18:12:51
我试图断言包装在另一个函数中的修补 request.get() 调用的输入。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测试脚本: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>如何断言传递给 mocked_response_200 的 url?mocked_response_200 在 test_get_data 中不是“已知的”。我已经在这里查看了其他帖子这一篇很接近,但答案使用了不同的补丁方法。任何帮助将不胜感激。
查看完整描述

3 回答

?
拉莫斯之舞

TA贡献1820条经验 获得超10个赞

您正在修补模块中的未知get对象request。您可能没有这样的模块或对象。


您需要在request被测模块中寻址对象。如果get_data位于模块中views,那么您需要在views.request.get此处打补丁:


@mock.patch('views.request.get', new=mock_response_200)

从mock.patch()文档:


目标应该是形式为 的字符串'package.module.ClassName'。目标被导入并且指定的对象替换为新对象,因此目标必须可从您正在调用的环境中导入patch()。目标是在执行装饰函数时导入的,而不是在装饰时导入。


我不是在这里使用的功能都不过。只需让模拟get()为您修补方法,然后配置该模拟对象。您当然可以将其委托给辅助函数:


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)

您还可以在这样的函数中创建一个魔术模拟对象,然后将该函数传递给new_callableon 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)

无论哪种方式,用于修补的对象request.get都test_get_data作为参数传入。


两种方法的演示(patch用作上下文管理器而不是装饰器,但原理是相同的:


>>> 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')]


查看完整回答
反对 回复 2021-12-09
?
慕婉清6462132

TA贡献1804条经验 获得超2个赞

首先,非常感谢@Ja8zyjits 和@Martijn Pieters 的帮助。

对我有用的解决方案如下:


@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)

我不能说我完全理解传递mock_response_200为'side_effect'和传递mock_get到之间的相互作用test_get_data。但是使用这种组合,我能够同时断言打补丁的 request.get 的输入并返回所需的响应,以防止在响应处理期间引发任何错误。


查看完整回答
反对 回复 2021-12-09
?
慕标5832272

TA贡献1966条经验 获得超4个赞

单元测试是一次测试一个基本组件。因此,内部调用的每个其他组件都需要在其他测试中进行测试。


如果您只想断言url正确传递,那么我建议不要使用new关键字


@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)

这将帮助您确定它是否被正确调用。在后面的测试中,使用new关键字来检查返回的响应是否正确处理。


这mock_process_response是一个MagicMock对象,将阻止process_response被调用,因此不需要mock_get定义json或被status_code定义。


编辑:为process_response.


查看完整回答
反对 回复 2021-12-09
  • 3 回答
  • 0 关注
  • 153 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信