Pytest monkeypatch不适用于导入的函数

2022-03-01 00:00:00 python pytest unit-testing

问题描述

假设项目中有两个包:some_packageanother_package

# some_package/foo.py:
def bar():
    print('hello')
# another_package/function.py
from some_package.foo import bar

def call_bar():
    # ... code ...
    bar()
    # ... code ...
我要测试another_package.function.call_bar模拟some_package.foo.bar,因为它有一些我要避免的网络I/O。

这是一个测试:

# 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
令我惊讶的是,它输出的是hello而不是patched。我试图调试这个东西,在测试中设置了IPDB断点。当我在断点后手动导入some_package.foo.bar并调用bar()时,我得到patched

在我的实际项目中,情况更加有趣。如果我在项目根目录中调用pytest,我的函数不会打补丁,但是当我指定tests/test_bar.py作为参数时-它可以工作。

据我了解,它与from some_package.foo import bar语句有关。如果它是在monkeypatching发生之前执行的,则修补失败。但是,在上面示例中的精简测试设置上,打补丁不能在两种情况下都起作用。

为什么它在命中断点后在IPDB REPL中工作?


解决方案

命名导入为对象创建新名称。如果然后替换对象的旧名称,则新名称不受影响。

导入模块并改用module.bar。它将始终使用当前对象。


编辑:

import module 

def func_under_test():
  module.foo()

def test_func():
   monkeypatch.setattr(...)
   func_under_test

相关文章