Python导入机制与pytest Mock冲突问题排查求助
问题原因及解决方案
为什么Mock会影响其他模块?
Python的模块是单例缓存机制:当你在src/mini_handler.py里import tempfile,再在src/mini_pdf_handler.py里import tempfile,这两个模块中的tempfile其实指向同一个全局模块对象。你用mocker.patch("src.mini_handler.tempfile.NamedTemporaryFile")时,本质是修改了这个全局tempfile模块的NamedTemporaryFile属性——所以所有用到tempfile.NamedTemporaryFile的模块都会被影响,包括mini_pdf_handler。
解决方法
方法1:修改导入方式(推荐)
把mini_handler.py的模块导入改成直接导入目标函数,让每个模块的函数引用独立,这样Patch只会作用于当前模块:
修改src/mini_handler.py:
# 直接导入函数,而非整个模块 from tempfile import NamedTemporaryFile from src.mini_pdf_handler import mini_pdf_handler def mini_handler(): tmp_file = NamedTemporaryFile() mini_pdf_handler() return tmp_file.name
修改测试代码,Patch该模块内的函数引用:
def test_mini_handler(mocker): mock_tempfile = mocker.MagicMock() mock_tempfile.return_value.name = 'outputs/output.pdf' # 现在Patch的是mini_handler模块内的NamedTemporaryFile引用,而非全局tempfile模块 mocker.patch("src.mini_handler.NamedTemporaryFile", side_effect=mock_tempfile) result = mini_handler() assert result == 'outputs/output.pdf'
这样mini_pdf_handler里的tempfile.NamedTemporaryFile会保持原始实现,不会被Mock。
方法2:不修改原代码的折中方案
如果不想改动业务代码,可以在测试中先保存全局tempfile.NamedTemporaryFile的原始值,调用mini_pdf_handler前恢复原始函数,完成后再重新Mock——但这种方式繁琐且容易出错:
import tempfile from src.mini_handler import mini_handler from src.mini_pdf_handler import mini_pdf_handler def test_mini_handler(mocker): mock_tempfile = mocker.MagicMock() mock_tempfile.return_value.name = 'outputs/output.pdf' # 保存原始函数 original_named_tempfile = tempfile.NamedTemporaryFile # Patch全局函数 mocker.patch("tempfile.NamedTemporaryFile", side_effect=mock_tempfile) # 执行mini_handler中依赖Mock的部分 tmp_file = tempfile.NamedTemporaryFile() # 恢复原始函数,确保mini_pdf_handler用的是真实实现 tempfile.NamedTemporaryFile = original_named_tempfile mini_pdf_handler() # 若后续还有需要,可重新Patch assert tmp_file.name == 'outputs/output.pdf'
显然方法1更简洁可靠,优先推荐。
内容的提问来源于stack exchange,提问作者Francesco Alongi
相关产品推荐
相关产品推荐

