pytest - monkeypatch关键字参数默认值

7

我想测试一个函数的默认行为。 我有以下内容:

# app/foo.py
DEFAULT_VALUE = 'hello'

def bar(text=DEFAULT_VALUE):
    print(text)

# test/test_app.py
import app

def test_app(monkeypatch):
    monkeypatch.setattr('app.foo.DEFAULT_VALUE', 'patched')
    app.foo.bar()
    assert 0

输出为hello,并不是我想要的。

一个解决方案是显式传递默认值:app.foo.bar(text=app.foo.DEFAULT_VALUE)

但有趣的是,当默认值为全局范围时,这似乎不是一个问题:

# app/foo.py
DEFAULT_VALUE = 'hello'

def bar():
    print(DEFAULT_VALUE)

输出结果为已修补

为什么会发生这种情况?是否有比显式传递默认值更好的解决方案?


可能是如何在Python中修补常量的重复问题。 - Michael H.
2个回答

6

函数默认值在 函数定义时 绑定。

当你进入测试代码时,该函数所在的模块已经被导入,通过对模块级别的常量进行猴子补丁已经为时过晚,不可能再更换默认值。该名称已经被解析。

一种解决方法是这样定义函数:

def bar(text=None):
    if text is None:
        text = DEFAULT_VALUE
    print(text)

现在默认值是在函数调用时查找的,这意味着对模块级别默认值的猴子补丁仍将起作用。

如果您不想修改函数定义,那么可以直接猴子补丁函数对象本身:

monkeypatch.setattr("app.foo.bar.__defaults__", ("test_hello",))

0
这是因为当您导入应用程序模块时,该模块在导入时被解释,因此一旦它被导入到foo中,该模块看起来像这样:

# app/foo.py
DEFAULT_VALUE = 'hello'

def bar(text='hello'):
    print(text)

当您调用函数时,函数内部的代码将被解释执行,这就解释了为什么您会看到猴子补丁的DEFAULT_VALUE。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接