Python 对模拟的相同调用链是否总是返回完全相同的模拟对象?

Python 对模拟的相同调用链是否总是返回完全相同的模拟对象?,python,unit-testing,mocking,monkeypatching,Python,Unit Testing,Mocking,Monkeypatching,代码: 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

代码:

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
从unittest.mock导入mock
mock=mock()
打印('mock.f():'),id(mock.f())
打印('mock.f().g().h():'),id(mock.f().g().h())
打印('mock():',id(mock()))
打印('mock().f():',id(mock().f()))
打印()
打印('mock.f():'),id(mock.f())
打印('mock.f().g().h():'),id(mock.f().g().h())
打印('mock():',id(mock()))
打印('mock().f():',id(mock().f()))
打印()
打印('mock.f(1):',id(mock.f(1)))
打印('mock.f(2.g(3.h(4)),id(mock.f(2.g(3.h(4)))
打印('mock(5):',id(mock(5)))
打印('mock(6.f(7):'),id(mock(6.f(7)))
打印()
输出:

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(): 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(): 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对象
  • 是否可以保证即使是具有不同参数的相同调用链,例如
    mock.f(2).g(3).h(4)
    也会返回与
    mock.f().g().h()相同的对象
  • 这两件事都有记录吗
背景:

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
我问这个问题的原因是,与其编写这样的代码,不如:

来自urllib导入请求
从unittest.mock导入mock,补丁
将补丁('urllib.request.urlopen')作为mock_urlopen:
mock\u urlopen.return\u value=mock()
mock_urlopen().getcode.return_值=200
assert request.urlopen(“”).getcode()==200
我可以编写如下所示的代码:

来自urllib导入请求
从unittest.mock导入mock,补丁
将补丁('urllib.request.urlopen')作为mock_urlopen:
mock_urlopen().getcode.return_值=200
assert request.urlopen(“”).getcode()==200

上面的例子过于简单,仅用于演示目的。我想保留一些独立的例子。但是如果我可以依赖这个特性,当函数调用链很长时,它将变得非常方便。这就是为什么我在寻找某种证明我可以依赖这种行为的参考或文档。

如果您查看文件
lib/python3.7/unittest/mock.py

def\uu getattr\uuu(self,name):
如果名称在{''u mock'u methods','u mock'u unsafe'}中:
提升属性错误(名称)
elif self.\u mock\u方法不是无:
如果名称不在self.\u mock\u方法中或名称不在\u all\u magics中:
raise AttributeError(“模拟对象没有属性%r”%name)
埃利夫是魔术(名字):
提升属性错误(名称)
如果不是自己,则模拟不安全:
如果name.startswith(('assert','assret')):
提升属性错误(名称)
结果=self.\u mock\u children.get(name)
如果结果被删除:
提升属性错误(名称)
elif结果为无:
包装=无
如果self.\u mock\u wrapps不是无:
#XXXX我们应该在不触发代码的情况下获取属性吗
#处决?
wrapps=getattr(self.\u mock\u wrapps,name)
结果=自我。\u获取\u孩子\u模拟(
parent=self,name=name,wrapps=wrapps,_new_name=name,
_新家长=自我
)
self.\u mock\u children[name]=结果
elif isinstance(结果,规格状态):
结果=创建_autospec(
result.spec、result.spec\u集、result.instance、,
result.parent,result.name
)
self.\u mock\u children[name]=结果
返回结果
如您所见,对象缓存在
\u mock\u children
dict中,因此每次调用都会返回对象。但数据将会更新。通过运行下面的代码可以看到

从unittest.mock导入mock
mock=mock()
a(10)
用(10)调用mock.a.assert_
a(2)
用(10)调用mock.a.assert_
结果呢

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)
回溯(最近一次呼叫最后一次):
文件“../workbench.py”,第8行,在
用(10)调用mock.a.assert_
文件“…lib/python3.7/unittest/mock.py”,第834行,在assert_中使用
从原因引发断言错误(_error_message())
AssertionError:预期调用:a(10)
实际通话:a(2)

因此,是的,对象将是相同的,但是如果查看文件
lib/python3.7/unittest/mock.py,对象将具有更新的值

def\uu getattr\uuu(self,name):
如果名称在{''u mock'u methods','u mock'u unsafe'}中:
提升属性错误(名称)
elif self.\u mock\u方法不是无:
如果名称不在self.\u mock\u方法中或名称不在\u all\u magics中:
raise AttributeError(“模拟对象没有属性%r”%name)
埃利夫是魔术(名字):
提升属性错误(名称)
如果不是自己,则模拟不安全:
如果name.startswith(('assert','assret')):
提升属性错误(名称)
结果=self.\u mock\u children.get(name)
如果结果被删除:
提升属性错误(名称)
elif结果为无:
包装=无
如果self.\u mock\u wrapps不是无:
#XXXX我们应该在不触发代码的情况下获取属性吗
#处决?
wrapps=getattr(self.\u mock\u wrapps,name)
结果=自我。\u获取\u孩子\u模拟(
parent=self,name=name,wrapps=wrapps,_new_name=name,
_新家长=自我
)
self.\u mock\u children[name]=结果
elif isinstance(结果,规格状态):
结果=创建_autospec(
result.spec、result.spec\u集、result.instance、,
result.parent,result.name
)
self.\u mock\u children[name]=结果
返回结果