模拟requests.post和requests.json解码器Python

15

我正在为使用requests库的模块创建测试套件。但是,我尝试模拟特定请求的多个不同返回值时遇到了问题。这是我不起作用的代码片段:

class MyTests(unittest.TestCase):

    @patch('mypackage.mymodule.requests.post') 
    def test_change_nested_dict_function(self, mock_post):
        mock_post.return_value.status_code = 200
        mock_post.return_value.json = nested_dictionary
        modified_dict = mymodule.change_nested_dict()
        self.assertEqual(modified_dict['key1']['key2'][0]['key3'], 'replaced_value')

我正在尝试模拟的函数:

import requests

def change_nested_dict():
    uri = 'http://this_is_the_endpoint/I/am/hitting'
    payload = {'param1': 'foo', 'param2': 'bar'}
    r = requests.post(uri, params=payload)

    # This function checks to make sure the response is giving the 
    # correct status code, hence why I need to mock the status code above
    raise_error_if_bad_status_code(r)

    dict_to_be_changed = r.json()

    def _internal_fxn_to_change_nested_value(dict):
        ''' This goes through the dict and finds the correct key to change the value. 
            This is the actual function I am trying to test above'''
        return changed_dict


    modified_dict = _internal_fxn_to_change_nested_value(dict_to_be_changed)

    return modified_dict

我知道简单的方法是不要有嵌套函数,但我只是展示整个函数代码的一部分。相信我,嵌套函数是必要的,我真的不想改变它。

我的问题是,我不知道如何模拟`requests.post`,然后设置状态码和内部JSON解码器的返回值。我也找不到绕过此问题的方法,因为我好像无法修补内部函数,这也将解决此问题。有人有任何建议/想法吗?非常感谢。


不需要嘲笑函数_internal_fxn_to_change_nested_value,我在你的测试中没有看到这个函数的任何参数,给出的答案似乎是正确的。 - Gang
4个回答

46

我来到这里,虽然我同意使用特定目的的库可能是更好的解决方案,但最终我做了以下操作

from mock import patch, Mock

@patch('requests.post')
def test_something_awesome(mocked_post):
    mocked_post.return_value = Mock(status_code=201, json=lambda : {"data": {"id": "test"}})

在进行单元测试时,这对我获取接收端的status_codejson()都起作用。

我写在这里是希望有人能从中受益。


4
这正是我想要的,简单易懂。 - Abdul Basit

18

当你对一个类进行mock时,每个子方法都会被设置为一个新的MagicMock,这又需要进行配置。因此,在这种情况下,您需要设置mock_postreturn_value以使子属性存在,并且还需要设置一个实际返回值,即:

mock_post.return_value.status_code.return_value = 200
mock_post.return_value.json.return_value = nested_dictionary

你可以通过查看所有内容的类型来了解这一点:

print(type(mock_post))
print(type(mock_post.json))

在两种情况下,类型都是<class 'unittest.mock.MagicMock'>


我喜欢这个,但我发现一个缺点是你必须在mock_post上调用.json而不是.json(),而@SRC的答案允许通过将返回值设置为lambda函数来进行函数调用。 - user3366016

2

第二个库已经迁移到 https://requests-mock.readthedocs.io/en/latest/。 - rleir

0
另一种方法是创建一个实际的响应对象,然后在原始模拟上执行configure_mock()
from requests import Response

class MyTests(unittest.TestCase):

    @patch('mypackage.mymodule.requests.post') 
    def test_change_nested_dict_function(self, mock_post):
        resp = Response()
        resp.status_code = 200
        resp.json = nested_dictionary
        mock_post.configure_mock(return_value=resp)
        ...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接