3

Python 3 模拟对象支持查询其调用的参数,是否也可以查询它们的调用返回的值?

我的特殊情况是我模拟 tempfile.mkdtemp,但作为副作用调用真正的 mkdtemp。我想在我的测试中获取创建的临时目录。

from unittest import mock
import shutil
import tempfile

from app import production_function


def mkdtemp(*args, **kwargs):
    dtemp = orig_mkdtemp(*args, **kwargs)
    return dtemp


orig_mkdtemp = tempfile.mkdtemp
patcher = mock.patch('tempfile.mkdtemp', name='tempfile.mkdtemp')
the_mock = patcher.start()
the_mock.side_effect = mkdtemp

# Call function under test
production_function()

assert the_mock.called
# Now, how to get the return value from the call to the_mock?

patcher.stop()
4

2 回答 2

2

不幸的是mock,模块不存储返回值(我通过调试器看了一下,没有任何痕迹)。您必须在返回 的值之前存储它side_effect

你可以用一个物体来处理肮脏的工作。例如,一个非常基础的实现可能是这样的:

class SideEffect():
    def __init__(self, n):
        self.values = iter(range(n))
        self.return_value = None

    def __call__(self):
        self.return_value = next(self.values)
        return self.return_value


a = Mock()
se = SideEffect(10)
a.side_effect = se

for x in range(10):
    v = a()
    assert v == se.return_value
    print("a()={}  return_value={}".format(v, se.return_value))

如果您想要一个更复杂side_effect的包装函数并处理参数和异常的示例,则可能是:

class GenericSideEffect():
    def __init__(self, f, *args, **kwargs):
        self.v_function = f
        self.args = args
        self.kwargs = kwargs
        self._return_value = Exception("Never Called")

    def __call__(self):
        try:
            self._return_value = self.v_function(*self.args, **self.kwargs)
            return self._return_value
        except Exception as e:
            self.return_value = e
            raise e

    @property
    def return_value(self):
        if isinstance(self._return_value, Exception):
            raise self._return_value
        return self._return_value

当然,您可以将其编写为装饰器并保留签名,但我认为该部分超出了该答案的范围。

于 2014-11-02T15:15:05.643 回答
1

如果行为是确定性的并且状态较少,您可以获得对您的模拟完成的调用列表并再次调用您感兴趣的嵌套调用以捕获结果。否则,我想您可以执行以下操作:

def mkdtemp_wrapper(result_storage):
    def mkdtemp(*args, **kwargs):
        dtemp = orig_mkdtemp(*args, **kwargs)
        result_storage.append(((*args,**kwargs),dtemp))
        return dtemp
    return mkdtemp

并以这种方式修改模拟:

results_values = []
the_mocks.side_effect = mkdtemp_wrapper(result_values)

然后您将在结果值中获得一对参数的列表,即结果。

希望能帮助到你。

于 2014-10-31T10:26:46.957 回答