如何在python中模拟嵌套/多层返回对象

Ric*_*c W 11 python nested mocking

我目前正在努力寻找一种模拟多层/嵌套返回值的好方法.换句话说,我想返回一个魔术模拟,然后返回一个带有它自己的设置返回值的魔术模拟.我发现这相对麻烦,我正在寻找一个更优雅和可维护的解决方案.

我正在尝试有效地测试以下代码.URL返回需要进一步处理的json字符串:

import json
from urllib.request import url open

def load_json():
    # first return value
    response = urlopen("http://someurl.com/api/getjson")
    # in turn, contains two nested return values for read and decode
        response_dict = json.loads(response.read().decode('utf-8'))
Run Code Online (Sandbox Code Playgroud)

到目前为止,这就是我嘲笑的方式,这非常不优雅并且使维护变得复杂:

class MyTestCase(TestCase):
    @patch('load_json_path.urlopen')
    def test_load_json(self, mock_urlopen):
        ### trying to simplify all of this
        # third nested return
        mock_decode = MagicMock(return_value='["myjsondata"]')
        # second nested return value
        mock_response = MagicMock()
        mock_response.read.return_value=mock_decode
        # first nested return value
        mock_urlopen.return_value = mock_response
        ### trying to simplify all of this            

        load_json()
Run Code Online (Sandbox Code Playgroud)

最后,我试图模拟的是来自解码函数的返回数据,它来自url open函数.这应该可以在一行中或以更简单的方式使用,也许使用输入方法.理想情况下,mock会在test_load_json函数中看起来像这样:

mock_urlopen.__enter__.loads.__enter__.decode.return_value = '["myjsondata"]'
Run Code Online (Sandbox Code Playgroud)

不幸的是,我似乎无法在模拟文档中找到任何有用的东西.任何帮助赞赏.

Ric*_*c W 23

结果证明这很容易并且有记录.但是,命名并不简单,需要知道自己在寻找什么.引用的模拟是链式调用,实际上是在模拟库中记录的.

在此示例中,mock_urlopen应如下所示:

    mock_urlopen.return_value.read.return_value.decode.return_value = '["myjsondata"]'
Run Code Online (Sandbox Code Playgroud)

这很好用.有关更多详细信息,请查看python doc:https://docs.python.org/3/library/unittest.mock-examples.html#mocking-chained-calls


Bha*_*rel 5

我已经为您提供了这门辅助课程:

from unittest.mock import Mock

class ProxyMock:
    """Put me for easy referral"""
    def __init__(self, mock, _first=True):
        self._mock_ = mock
        self._first_ = _first

    def __getattr__(self, name):
        if self._first_:
            new_mock = getattr(self._mock_, name)
        else:
            new_mock = getattr(self._mock_.return_value, name)
        return ProxyMock(new_mock, _first=False)

    def __setattr__(self, name, value):
        if name in ("_mock_", "_first_"):
            return super().__setattr__(name, value)
        setattr(self._mock_, name, value)

a = Mock()
ProxyMock(a).b.c.return_value = 123

assert a.b().c() == 123
Run Code Online (Sandbox Code Playgroud)