pytest -monkeypatch 关键字参数默认值

Nat*_*nes 5 python monkeypatching pytest

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

# app/foo.py
DEFAULT_VALUE = 'hello'

def bar(text=DEFAULT_VALUE):
    print(text)
Run Code Online (Sandbox Code Playgroud)
# test/test_app.py
import app

def test_app(monkeypatch):
    monkeypatch.setattr('app.foo.DEFAULT_VALUE', 'patched')
    app.foo.bar()
    assert 0
Run Code Online (Sandbox Code Playgroud)

输出是hello; 不是我想要的。

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

但我觉得有趣的是,当默认为全局范围时,这似乎不是问题:

# app/foo.py
DEFAULT_VALUE = 'hello'

def bar():
    print(DEFAULT_VALUE)
Run Code Online (Sandbox Code Playgroud)

输出是 patched

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

wim*_*wim 6

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

当您进入测试代码时,定义该函数的模块已经被导入,并且通过在模块级别常量上进行猴子修补来交换默认值已经太晚了。这个名字已经被解决了。

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

def bar(text=None):
    if text is None:
        text = DEFAULT_VALUE
    print(text)
Run Code Online (Sandbox Code Playgroud)

现在,在函数调用时查找默认值,这意味着模块级别默认值上的猴子补丁仍然有效。

如果您不喜欢修改函数定义,那么您可以对函数对象本身进行 Monkeypatch:

monkeypatch.setattr("app.foo.bar.__defaults__", ("test_hello",))
Run Code Online (Sandbox Code Playgroud)