You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确使用pytest-mock并解决模块导入顺序导致的Mock失效问题

pytest-mock全局导入场景下mock失效的解决办法

问题场景

你的项目结构如下:

src
  pull
    app.py
  utils
    sftp.py
tests
  test_pull.py

app.py通过全局导入引入sftp.py的list_files:

from src.utils.sftp import list_files

def handler():
    response = list_files()

测试时你尝试mocksrc.utils.sftp.list_files,但发现完全不生效,只有把导入改成函数内的局部导入才能正常mock,但你不想依赖这种导入方式。

核心原因

当你在测试文件里执行from src.pull.app import handler时,app.py已经完成了全局导入,list_files的引用被复制到了app.py的命名空间中。此时你mock原模块src.utils.sftp.list_files,只是替换了原模块里的函数对象,但app.py里的list_files还是指向原来的函数,所以handler调用的依然是未被mock的原函数。

正确的Mock方案

直接mockapp.py中已经导入的list_files引用,也就是把patch的目标路径改成src.pull.app.list_files:

from src.pull.app import handler

def test_pulling(mocker):
    # 针对app模块内的list_files引用进行mock
    mock_list_files = mocker.patch('src.pull.app.list_files', return_value=["something"])
    handler()
    # 可选:验证mock函数被调用过
    mock_list_files.assert_called_once()

原理说明

from ... import ...本质是将目标对象的引用复制到当前模块。要让handler使用mock后的函数,必须替换app.py模块里保存的那个引用,而不是原模块里的函数。这才是mock这类导入场景的正确姿势,完全不需要修改业务代码的导入方式。

另一种可选方案(不推荐)

如果你不想修改patch路径,也可以先mock再导入handler,这样全局导入时会直接拿到mock后的对象:

def test_pulling(mocker):
    # 先mock再导入handler
    mocker.patch('src.utils.sftp.list_files', return_value=["something"])
    from src.pull.app import handler
    handler()

但这种方式依赖测试内的导入顺序,不如第一种方案直观可靠,不推荐作为常规写法。

内容的提问来源于stack exchange,提问作者reikje

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 14:43:29