Pytest monkeypatch无法处理导入的函数

时间:2015-07-09 00:18:37

标签: python unit-testing pytest

假设项目中有两个包: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中有效?

5 个答案:

答案 0 :(得分:22)

Ronny's answer工作时会强制您更改应用程序代码。一般来说,为了测试,你不应该这样做。

相反,您可以在第二个包中显式修补该对象。这在docs for the unittest module中提到。

monkeypatch.setattr('another_package.bar', lambda: print('patched'))

答案 1 :(得分:8)

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

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

修改

import module 

def func_under_test():
  module.foo()

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

答案 2 :(得分:2)

作为Alex said,,您不应该为测试重写代码。我遇到的陷阱是要修补的路径。

给出代码:

app / handlers / tasks.py

redisTemplate.opsForValue().set(key, value, 1, TimeUnit.MINUTES);

您第一个获得from auth.service import check_user def handle_tasks_create(request): check_user(request.get('user_id')) create_task(request.body) return {'status': 'success'} 补丁的本能,就像这样:

check_user

但是您要做的是在monkeypatch.setattr('auth.service.check_user', lambda x: return None) 中修补实例。这可能就是您想要的:

tasks.py

虽然给出的答案已经很好,但我希望这会带来更完整的上下文。

答案 3 :(得分:0)

OP的正确答案:

library(rio)
Qualtrics <- list.files(directory)   # This gives you your input files
# Split off the extension and add .csv extension
QualtricsCSV <- paste0(sapply(strsplit(Qualtrics, "\\."), "[", 1), ".csv")
setwd(directory)
for (i in seq(length(x)) {
     convert(Qualtrics[i], QualtricsCSV[i])
}

答案 4 :(得分:0)

您的函数未得到修补的另一个可能原因是您的代码正在使用多处理。

在macOS上,新进程的默认启动方法已从fork更改为spawn。如果使用spawn,则会启动一个全新的Python解释器进程,而忽略您最近修补的函数。

修复:将默认启动方法设置为fork

import multiprocessing

multiprocessing.set_start_method('fork', force=True)

您可以将此代码段添加到conftest.py文件夹内的tests/中。