繁体   English   中英

Python 模拟方法调用参数显示列表的最后状态

[英]Python mock method call arguments display the last state of a list

我有一个将列表作为参数的函数。 该函数被多次调用,并且每次更新一些列表值时。 我用来捕获调用参数的模拟对象始终显示列表中所有调用参数的最新值。 以下代码显示了问题。

from mock import MagicMock

def multiple_calls_test():
    m = MagicMock()
    params = [0, 'some_fixed_value', 'some_fixed_value']
    for i in xrange(1,10):
        params[0] = i
        m(params)
    for args in m.call_args_list:
        print args[0][0]

multiple_calls_test()

这是输出,注意所有调用都将 9 作为第一个列表元素。

[9, 'some_fixed_value', 'some_fixed_value']
[9, 'some_fixed_value', 'some_fixed_value']
[9, 'some_fixed_value', 'some_fixed_value']
[9, 'some_fixed_value', 'some_fixed_value']
[9, 'some_fixed_value', 'some_fixed_value']
[9, 'some_fixed_value', 'some_fixed_value']
[9, 'some_fixed_value', 'some_fixed_value']
[9, 'some_fixed_value', 'some_fixed_value']
[9, 'some_fixed_value', 'some_fixed_value']

有没有办法强制模拟对象制作列表参数的副本,而不是保存对实际列表的引用? 或者其他一些方法来为每个方法执行断言正确的值? 谢谢。

不幸的是,这看起来是mock库的一个缺点,从代码来看,如果不修补模拟库本身,这似乎是不可能的。 但是,看起来有一种相当轻量级的方法可以做到这一点,以获得您正在寻找的效果:

import copy
from mock import MagicMock


class CopyArgsMagicMock(MagicMock):
    """
    Overrides MagicMock so that we store copies of arguments passed into calls to the
    mock object, instead of storing references to the original argument objects.
    """

    def _mock_call(_mock_self, *args, **kwargs):
        args_copy = copy.deepcopy(args)
        kwargs_copy = copy.deepcopy(kwargs)
        return super(CopyArgsMagicMock, self)._mock_call(*args_copy, **kwargs_copy)

然后(说明显而易见)只需将您的MagicMock替换为CopyArgsMagicMock ,您应该会看到所需的行为。

请注意,这仅针对所提供的用例进行了测试,因此这可能不是该问题的完整而强大的解决方案,但希望它被证明是有用的。

对于 python 3.8,接受的解决方案对我来说不再适用。
但是,官方python文档中有一个解决方案:
https://docs.python.org/3/library/unittest.mock-examples.html#coping-with-mutable-arguments
您必须向下滚动一点才能找到以下内容:

另一种方法是创建一个 Mock 或 MagicMock 的子类来复制(使用 copy.deepcopy())参数。 这是一个示例实现:

from copy import deepcopy
class CopyingMock(MagicMock):
    def __call__(self, /, *args, **kwargs):
        args = deepcopy(args)
        kwargs = deepcopy(kwargs)
        return super(CopyingMock, self).__call__(*args, **kwargs)

这对我来说适用于 python 3.8。

试试 m.mock_calls。 这列出了进行的所有调用。 我认为这应该有效:

>>> from unittest.mock import MagicMock, call
>>> m = MagicMock()
>>> m('abc')
<MagicMock name='mock()' id='2634881401576'>
>>> m('def')
<MagicMock name='mock()' id='2634881401576'>
>>> call('abc') in m.mock_calls
True
>>> call('ghi') in m.mock_calls
False

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM