我想测试一个函数的默认行为。我有以下内容:
# 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)
输出为patched
。
为什么会这样?还有比明确传递默认值更好的解决方案吗?
答案 0 :(得分:1)
默认功能在函数定义时间绑定。
在您进入测试代码之时,定义功能的模块已经导入,现在通过在模块级别常量上进行monkeypatching换出默认值已经太迟了。这个名字已经解决了。
一种解决方法是定义如下函数:
def bar(text=None):
if text is None:
text = DEFAULT_VALUE
print(text)
现在,默认值是在函数 call 时查找的,这意味着在模块级别default上的Monkeypatch仍然可以工作。
如果您不想修改函数定义,则可以对函数对象本身进行猴子修补:
monkeypatch.setattr("app.foo.bar.__defaults__", ("test_hello",))
答案 1 :(得分:0)
这是因为,当您导入应用程序模块时,模块在导入时会得到解释,因此一旦将其导入到foo中,模块将如下所示:
# app/foo.py
DEFAULT_VALUE = 'hello'
def bar(text='hello'):
print(text)
调用函数时,将解释函数内部的代码,这解释了为什么您看到猴子修补了DEFAULT_VALUE