Pytest中mock sys.version_info触发MagicMock转换报错如何解决
问题根源
你遇到的报错本质是默认生成的MagicMock对象没有实现sys.version_info的核心行为:sys.version_info是namedtuple类型的实例,既支持下标索引访问、也支持直接和元组做大小比较、还支持major/minor属性访问,默认的MagicMock没有实现这些逻辑,所以会触发类型错误。
解决方案
不需要用默认的MagicMock实例,直接在patch的时候通过new参数传入你需要模拟的版本值即可,分两种场景处理:
场景1:你的版本判断只用了下标访问或者直接和元组比较
如果代码里是sys.version_info < (3,7)或者sys.version_info[0]/sys.version_info[1],直接传入普通元组即可:
def test_bad_python_version(capsys) -> None: import sys import mock import pytest from quendor.__main__ import main # 直接将version_info替换为代表3.5版本的元组 with mock.patch.object(sys, "version_info", new=(3, 5)): # 捕获低版本触发的SystemExit异常 with pytest.raises(SystemExit) as pytest_wrapped_e: main() # 可选校验退出码为1 assert pytest_wrapped_e.value.code == 1 terminal_text = capsys.readouterr() assert "Quendor requires Python 3.7 or later" in terminal_text.err
场景2:你的版本判断用到了major/minor属性访问
如果代码里有sys.version_info.major/sys.version_info.minor的写法,普通元组没有对应的属性,传入符合version_info结构的namedtuple即可:
from collections import namedtuple # 构造和sys.version_info结构一致的namedtuple VersionInfo = namedtuple('VersionInfo', ['major', 'minor', 'micro', 'releaselevel', 'serial']) def test_bad_python_version(capsys) -> None: import sys import mock import pytest from quendor.__main__ import main # 模拟3.5版本的version_info对象 mock_version = VersionInfo(3, 5, 0, 'final', 0) with mock.patch.object(sys, "version_info", new=mock_version): with pytest.raises(SystemExit): main() terminal_text = capsys.readouterr() assert "Quendor requires Python 3.7 or later" in terminal_text.err
补充说明
你之前mockplatform.python_version用return_value是因为python_version是可调用的方法,而sys.version_info是属性,不需要return_value参数,直接用new指定替换值即可。
内容的提问来源于stack exchange,提问作者Jeff Nyman
相关产品推荐
相关产品推荐

