This is discussed in the documentation section 26.6.3.7. Coping with mutable arguments.
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
.
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.
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: 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])
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…