我们如何确保 Mock.call_args_list 中的调用包含具有与调用 Mock 对象时相同状态的参数的调用?

2022-01-08 00:00:00 python mocking unit-testing

问题描述

from mock import Mock
j = []
u = Mock()
u(j)
# At this point u.call_args_list == [call([])]
print u.call_args_list
j.append(100)
# At this point u.call_args_list == [call([100])], but I expect it to be [call([])], since it was never called when j had a value of 100 in it
print u.call_args_list

我的问题是如何确保 u.call_args_list 中的调用在调用模拟时而不是在检查模拟参数时包含所有对象的状态?

My question is how do I ensure that the calls in u.call_args_list contain the states of all objects at the time of calling the mock rather than at the time of checking the arguments of the mock?

我目前正在使用 mock==1.0.1.


解决方案

这在文档部分进行了讨论 26.6.3.7.处理可变参数.

This is discussed in the documentation section 26.6.3.7. Coping with mutable arguments.

不幸的是,他们对这个问题并没有任何优雅的解决方案!推荐的解决方法是使用 side_effect 从可变参数中复制元素.

Unfortunately, they don't really have any elegant solution to the issue! The recommended workaround is copying elements from the mutable arguments by using side_effect.

如果您为模拟提供了一个 side_effect 函数,那么将使用与模拟相同的参数调用 side_effect.这使我们有机会复制参数并将它们存储起来以供以后断言.

If you provide a side_effect function for a mock then side_effect will be called with the same args as the mock. This gives us an opportunity to copy the arguments and store them for later assertions.

在我看来,实施起来有些混乱.如果您在多个地方需要该功能,您可能更愿意继承 Mock 并直接添加该功能:

It's somewhat messy to implement, in my opinion. If you need the capability in multiple places, you may prefer to subclass Mock and add the feature directly:

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)

<小时>

2017:它现在可以在第三方发行版中使用(pip install copyingmock).


2017: It's now available in a third-party distribution (pip install copyingmock).

>>> from copyingmock import CopyingMock
>>> mock = CopyingMock()
>>> list_ = [1,2]
>>> mock(list_)
<CopyingMock name='mock()' id='4366094008'>
>>> list_.append(3)
>>> mock.assert_called_once_with([1,2])
>>> mock.assert_called_once_with(list_)

AssertionError: Expected call: mock([1, 2, 3])
Actual call: mock([1, 2])

相关文章