Glu*_*eon 21 python unit-testing pytest
假设项目中有两个包:some_package和another_package.
# some_package/foo.py:
def bar():
print('hello')
Run Code Online (Sandbox Code Playgroud)
# another_package/function.py
from some_package.foo import bar
def call_bar():
# ... code ...
bar()
# ... code ...
Run Code Online (Sandbox Code Playgroud)
我想测试another_package.function.call_bar模拟,some_package.foo.bar因为它有一些I/OI希望避免的网络.
这是一个测试:
# tests/test_bar.py
from another_package.function import call_bar
def test_bar(monkeypatch):
monkeypatch.setattr('some_package.foo.bar', lambda: print('patched'))
call_bar()
assert True
Run Code Online (Sandbox Code Playgroud)
令我惊讶的是它输出hello而不是patched.我尝试调试这个东西,在测试中放置一个IPDB断点.当我some_package.foo.bar在断点后手动导入并调用bar()我得到patched.
在我的真实项目中,情况更加有趣.如果我在项目根目录中调用pytest,我的函数不会被修补,但是当我指定tests/test_bar.py为参数时 - 它可以工作.
据我所知,它与from some_package.foo import bar声明有关.如果在monkeypatching发生之前执行它,那么修补失败.但是在上面的例子中的压缩测试设置中,修补在两种情况下都不起作用.
为什么在遇到断点后它在IPDB REPL中工作?
Ale*_*lex 29
虽然Ronny的回答有效,但它迫使您更改应用程序代码.一般来说,为了测试,你不应该这样做.
相反,您可以显式修补第二个包中的对象.这在unittest模块的文档中提到.
monkeypatch.setattr('another_package.bar', lambda: print('patched'))
Run Code Online (Sandbox Code Playgroud)
ala*_*ock 16
正如亚历克斯所说,你不应该为你的测试重写你的代码。我遇到的问题是要修补的路径。
鉴于代码:
应用程序/处理程序/tasks.py
from auth.service import check_user
def handle_tasks_create(request):
check_user(request.get('user_id'))
create_task(request.body)
return {'status': 'success'}
Run Code Online (Sandbox Code Playgroud)
你对monkeypatch 的第一直觉check_user,像这样:
monkeypatch.setattr('auth.service.check_user', lambda x: return None)
Run Code Online (Sandbox Code Playgroud)
但是您要做的是在tasks.py. 可能这就是你想要的:
monkeypatch.setattr('app.handlers.tasks.check_user', lambda x: return None)
Run Code Online (Sandbox Code Playgroud)
虽然给出的答案已经很好,但我希望这能带来更完整的背景。
命名导入会为对象创建新名称.如果您随后替换对象的旧名称,则新名称不受影响.
导入模块并module.bar改为使用.这将始终使用当前对象.
编辑:
import module
def func_under_test():
module.foo()
def test_func():
monkeypatch.setattr(...)
func_under_test
Run Code Online (Sandbox Code Playgroud)