代码:
from unittest.mock import Mock
mock = Mock()
print('mock.f():', id(mock.f()))
print('mock.f().g().h():', id(mock.f().g().h()))
print('mock():', id(mock()))
print('mock().f():', id(mock().f()))
print()
print('mock.f():', id(mock.f()))
print('mock.f().g().h():', id(mock.f().g().h()))
print('mock():', id(mock()))
print('mock().f():', id(mock().f()))
print()
print('mock.f(1):', id(mock.f(1)))
print('mock.f(2).g(3).h(4):', id(mock.f(2).g(3).h(4)))
print('mock(5):', id(mock(5)))
print('mock(6).f(7):', id(mock(6).f(7)))
print()
输出:
mock.f(): 4483288208
mock.f().g().h(): 4483354192
mock(): 4483368976
mock().f(): 4483708880
mock.f(): 4483288208
mock.f().g().h(): 4483354192
mock(): 4483368976
mock().f(): 4483708880
mock.f(1): 4483288208
mock.f(2).g(3).h(4): 4483354192
mock(5): 4483368976
mock(6).f(7): 4483708880
观察:
输出表明,无论我们进行了多少次调用,对模拟的指定链式函数调用在程序的生命周期内总是返回相同的对象。
比如第一次调用mock.f().g().h(),第二次调用mock.f().g().h( ),甚至使用不同参数的第三次调用 mock.f(2).g(3).h(4) 也返回完全相同的对象。
问题:
- 我们可以依赖这种行为吗?是否保证在程序的生命周期内,
mock.f().g().h()会返回完全相同的模拟对象? - 是否可以保证即使具有不同参数的相同调用链,例如,
mock.f(2).g(3).h(4)也将返回与 a 相同的对象mock.f().g().h()? - 这些东西都记录在某处吗?
背景:
我问这个问题的原因是为了不写这样的代码:
from urllib import request
from unittest.mock import Mock, patch
with patch('urllib.request.urlopen') as mock_urlopen:
mock_urlopen.return_value = Mock()
mock_urlopen().getcode.return_value = 200
assert request.urlopen('').getcode() == 200
我可以编写这样的代码:
from urllib import request
from unittest.mock import Mock, patch
with patch('urllib.request.urlopen') as mock_urlopen:
mock_urlopen().getcode.return_value = 200
assert request.urlopen('').getcode() == 200
以上示例过于简单,仅供演示之用。我想保留独立的示例。但是如果我可以依赖这个特性,当函数调用链很长时,它会变得非常方便。这就是为什么我正在寻找表明我可以依赖此行为的某种引用或文档。
请您参考如下方法:
如果您查看文件 lib/python3.7/unittest/mock.py
def __getattr__(self, name):
if name in {'_mock_methods', '_mock_unsafe'}:
raise AttributeError(name)
elif self._mock_methods is not None:
if name not in self._mock_methods or name in _all_magics:
raise AttributeError("Mock object has no attribute %r" % name)
elif _is_magic(name):
raise AttributeError(name)
if not self._mock_unsafe:
if name.startswith(('assert', 'assret')):
raise AttributeError(name)
result = self._mock_children.get(name)
if result is _deleted:
raise AttributeError(name)
elif result is None:
wraps = None
if self._mock_wraps is not None:
# XXXX should we get the attribute without triggering code
# execution?
wraps = getattr(self._mock_wraps, name)
result = self._get_child_mock(
parent=self, name=name, wraps=wraps, _new_name=name,
_new_parent=self
)
self._mock_children[name] = result
elif isinstance(result, _SpecState):
result = create_autospec(
result.spec, result.spec_set, result.instance,
result.parent, result.name
)
self._mock_children[name] = result
return result
如您所见,该对象缓存在 _mock_children 字典中。因此每次调用都会返回对象。但是数据会更新。你可以通过运行下面的代码看到
from unittest.mock import Mock
mock = Mock()
mock.a(10)
mock.a.assert_called_with(10)
mock.a(2)
mock.a.assert_called_with(10)
结果
Traceback (most recent call last):
File ".../workbench.py", line 8, in <module>
mock.a.assert_called_with(10)
File ....lib/python3.7/unittest/mock.py", line 834, in assert_called_with
raise AssertionError(_error_message()) from cause
AssertionError: Expected call: a(10)
Actual call: a(2)
所以,是的,对象将是相同的,但对象将具有更新的值
