提问人:Aage Torleif 提问时间:11/7/2023 最后编辑:juanpa.arrivillagaAage Torleif 更新时间:11/7/2023 访问量:76
为什么在基类上修补类方法会影响子类?
Why does patching a classmethod on a base class effect subclasses?
问:
我在基类上修补 a 时遇到了问题。问题在于修补基类效应,调用子类上的类方法。classmethod
我希望它产生输出,但是在调用 classmethod 之前调用的结果是 .["foo", "bar"]
mocker.spy(Foo, "wrapper")
["foo", "foo"]
这是怎么回事?
def test_patching_parent_class(mocker):
"""
pip install pytest pytest-mock
"""
calls = []
class Foo:
def func2(self):
calls.append("foo")
@classmethod
def wrapper(cls):
cls().func2()
class Bar(Foo):
def func2(self):
calls.append("bar")
# Remove this line it will produce the expected result
spy = mocker.spy(Foo, "wrapper")
Foo.wrapper()
Bar.wrapper()
assert calls == ["foo", "bar"]
答:
2赞
juanpa.arrivillaga
11/7/2023
#1
这似乎是 .这是来源的链接:pytest_mock.MockFixture.spy
你可以看到,它基本上是从对象(在本例中为类)中检索“方法”,但由于是一个类,并且引用了一个,现在这是一个绑定方法:Foo
obj
Foo
name
classmethod
method = getattr(obj, name)
它与 绑定。这就是类方法的工作方式。它们的作用类似于在实例上调用的常规函数,当它们本身在类上被调用时。如果这是一个常规函数,那么它只是普通函数(因为尝试检索类上的函数属性,而不是在实例上,通常只会返回函数本身)。然后,它创建一个调用该方法的包装器:Foo
method
def wrapper(*args, **kwargs):
spy_obj.spy_return = None
spy_obj.spy_exception = None
try:
r = method(*args, **kwargs)
except BaseException as e:
spy_obj.spy_exception = e
raise
else:
spy_obj.spy_return = r
return r
这个包装器是用模拟对象修补的:
spy_obj = self.patch.object(obj, name, side_effect=wrapped, autospec=autospec)
它充当您的“间谍”。
但同样,它与 .因此,无论如何调用,它的第一个参数总是绑定 ,从不 。所以解析为而不是.Foo
wrapper
Foo
Bar
cls.func2
Foo.func2
Bar.func2
评论
0赞
blhsing
11/7/2023
我不认为是问题所在。如果您简单地替换为 (参见演示),OP 的代码就会起作用。由于只是一个薄薄的包装器,这表明这是包装器所做的事情导致了错误,我还没有弄清楚,但绝对不是这里的问题。pytest_mock.MockFixture.spy
MockerFixture._Patcher.object
unittest.mock.patch.object
MockerFixture._Patcher.object
unittest.mock._patch_object
pytest_mock.MockFixture.spy
0赞
blhsing
11/7/2023
那为什么用代替代替不是问题呢?side_effect=wrapped
unittest.mock.patch.object
MockerFixture._Patcher.object
0赞
juanpa.arrivillaga
11/7/2023
@blhsing如果我设置了一个断点,然后不再返回一个对象,还不知道为什么。因此,他们不再被“监视”。Foo.wrapper
Bar.wrapper
Mock
0赞
blhsing
11/7/2023
是的,我的断点揭示了同样多的内容。我自己也看不出为什么。好挠头。
1赞
blhsing
11/7/2023
啊,因为 never got ed 返回的对象仍然绑定并且没有成为对象。两种实现之间的不兼容让我失望。因此,您的答案是正确的。+1_patch
unittest.mock.patch.object
start
Bar.wrapper
Bar
Mock
patch.object
评论
Bar.wrapper
Bar.wrapper()
Foo.wrapper
Foo.wrapper
calls
["foo", "bar"]