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

时间:2016-09-12 19:07:41

标签: python nested mocking

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

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

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

这就是我到目前为止嘲笑这个问题的方法,它非常不优雅,使维护变得复杂:

mock_urlopen.__enter__.loads.__enter__.decode.return_value = '["myjsondata"]'

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

max-width

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

2 个答案:

答案 0 :(得分:21)

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

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

    mock_urlopen.return_value.read.return_value.decode.return_value = '["myjsondata"]'

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

答案 1 :(得分:3)

我已经为你做了一个帮助类:

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