为何修补基类的classmethod会对子类产生影响?
问题原因
当你使用mocker.spy(Foo, "wrapper")时,pytest-mock会将Foo.wrapper替换为一个Mock对象,这个Mock包裹的是已经绑定到Foo类的classmethod方法。子类Bar继承的wrapper实际指向的是Foo中被替换后的Mock,调用Bar.wrapper()时,Mock会直接调用绑定到Foo的原始wrapper方法,导致cls参数始终为Foo,因此两次调用都会创建Foo实例,最终calls结果变成["foo", "foo"]。
解决方案
方案一:Spy ClassMethod的原始函数
直接针对classmethod的原始函数进行spy,保留classmethod的descriptor结构,确保子类调用时能正确传递自身类作为cls参数:
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") # 修改spy逻辑,针对原始函数而非绑定方法 original_wrapper = Foo.wrapper.__func__ spy = mocker.patch.object(Foo.wrapper, "__func__", wraps=original_wrapper) Foo.wrapper() Bar.wrapper() assert calls == ["foo", "bar"]
方案二:手动实现Spy逻辑
如果不想依赖patch的内部逻辑,可以手动替换classmethod的原始函数,自定义spy行为:
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") # 手动实现spy,保留classmethod的绑定逻辑 original_wrapper = Foo.wrapper.__func__ def spy_wrapper(cls, *args, **kwargs): # 这里可以添加自定义的spy逻辑,比如记录调用参数 return original_wrapper(cls, *args, **kwargs) Foo.wrapper.__func__ = spy_wrapper Foo.wrapper() Bar.wrapper() assert calls == ["foo", "bar"]
内容的提问来源于stack exchange,提问作者Aage Torleif
相关产品推荐
相关产品推荐

