我在修补基类上的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"]

推荐答案

这似乎是pytest_mock.MockFixture.spy中的一个错误.Here is a link to the source:

您可以看到,它基本上是从对象(在本例中是类Foo)检索"方法",但是因为obj是一个类,Foo,而name引用classmethod,所以现在是bound method:

method = getattr(obj, name)

bound乘以Foo.This is how classmethods work.当它们本身在类上被调用时,它们的作用类似于在实例上调用常规函数.如果这是一个正则函数,method就是普通函数(从trying to retrieve a function attribute on a class not on an instance normally a just returns the function itself开始).然后,它创建一个调用该方法的包装器:

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

这个包装器就是用mock对象修补的:

spy_obj = self.patch.object(obj, name, side_effect=wrapped, autospec=autospec)

它充当你的"间谍".

但再说一次,是bound乘以Foo.因此,无论如何调用wrapper,它的第一个参数总是与Foo绑定,而不是Bar.因此,cls.func2解析为Foo.func2,而不是Bar.func2.

Python相关问答推荐

已安装' owiener ' Python模块,但在导入过程中始终没有名为owiener的模块

合并同名列,但一列为空,另一列包含值

sys.modulesgo 哪儿了?

使用Python Cerberus初始化一个循环数据 struct (例如树)(v1.3.5)

过滤绕轴旋转的螺旋桨

使文本输入中的文本与标签中的文本相同

DuckDB将蜂巢分区插入拼花文件

如何通过多2多字段过滤查询集

从webhook中的短代码(而不是电话号码)接收Twilio消息

使用mySQL的SQlalchemy过滤重叠时间段

当多个值具有相同模式时返回空

. str.替换pandas.series的方法未按预期工作

2D空间中的反旋算法

DataFrames与NaN的条件乘法

Pandas计数符合某些条件的特定列的数量

如何更新pandas DataFrame上列标题的de值?

旋转多边形而不改变内部空间关系

处理Gekko的非最优解

不允许 Select 北极滚动?

使用python playwright从 Select 子菜单中 Select 值