假设项目中有两个包:some_package
和another_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中有效?
答案 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/
中。