【问题标题】:How do I patch a sys attribute using a decorator?如何使用装饰器修补 sys 属性?
【发布时间】:2018-10-22 11:34:56
【问题描述】:

我有一个依赖于 Python 版本的函数。我想通过模拟sys.version 信息在单元测试中对此进行测试。以下代码有效:

def test_python_version(self):
    with patch("sys.version_info", [3]):
        my_func()
    with patch("sys.version_info", [2]):
        my_func()

我想使用装饰器,但是下面的代码不起作用。为什么是这样?如何设置传递给我的测试的 MagicMock 对象的值?谢谢

@patch("sys.version_info")
def test_python_version(self, mock_version_info):
        mock_version_info.return_value = [3]
        my_func()
        mock_version_info.return_value = [2]
        my_func()

my_func 尝试执行if sys.version_info[0] < 3 时,TypeError: '<' not supported between instances of 'MagicMock' and 'int' 失败。

【问题讨论】:

  • 修正了我的答案,不便之处敬请见谅。

标签: python python-3.x mocking python-unittest


【解决方案1】:

编辑:

是的,它有效。原始函数返回一个带有majorminormicro 字段的命名元组。您可以通过构建自己的命名元组来模仿它。当您使用int 索引访问时,我只是使用一个简单的元组。您的代码的问题在于您使用 [0] 编制索引的方式不正确。

编辑2:

正如 Zaur Nasibov 指出的那样,sys.version_info 不是一个函数,所以我的代码虽然在模拟中看起来不错(正如 GenError 也发现的那样),但我的代码是错误的。我做了一些修改来修复它(请参阅 GenError 答案以了解使用 PropertyMock 的替代方法)

import sys
from unittest.mock import patch

def my_func():
    version = sys.version_info # removed the ()
    print('Detected version:', version)
    if version[0] < 3:
        print('Error: Python 3 required.')

@patch('__main__.sys')
def test_python_version(mock_sys):
        mock_sys.version_info = (3,6,2)
        my_func()
        print()
        mock_sys.version_info = (2,7,0)
        my_func()


test_python_version()

输出

Detected version: (3, 6, 2)

Detected version: (2, 7, 0)
Error: Python 3 required.

【讨论】:

  • my_func() 有一行:if sys.version_info[0] &lt; 3 导致TypeError: '&lt;' not supported between instances of 'MagicMock' and 'int'
  • @pkiller162 你在[0] 之前错过了()。查看我的编辑。
  • sys.version_info 不可调用。 sys 模块应该被修补(参见@GenError 的答案)。
  • 感谢您的评论@ZaurNasibov。我刚刚确定了答案。模拟擅长使代码看起来可以工作:)
【解决方案2】:

我通过 progmatico 找到了答案,但它对我来说有一个严重的问题,因为它需要调用 sys.version_info() 而不是 sys.version_info 如果正常运行会破坏代码。

例子:

#filename: a.py
import sys
def do_something():
    if sys.version_info > (3,5):
        print('Python 3.5 or newer')
    else:
        print('Python pre 3.5')

现在,如果我想在单元测试中测试这两种情况,@patch("sys.version_info") 将导致 OP 给出的错误。如果不使用模拟,将do_something() 更改为使用sys.version_info() 会破坏它。

测试文件:

#filename: test_a.py
from unittest.mock import patch, PropertyMock
import a

@patch('a.sys')
def test_a(mock_sys):
    type(mock_sys).version_info = PropertyMock(return_value=(3,4))
    a.do_something() # will show mocked python version

所以你必须mock模块a中导入的sys模块,并在上面设置PropertyMock

【讨论】:

  • 感谢您在我的回答中指出问题。修复后看看它。如果您不想使用,则不必使用 PropertyMock。
猜你喜欢
  • 2021-07-19
  • 1970-01-01
  • 2023-03-20
  • 2021-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-26
  • 1970-01-01
相关资源
最近更新 更多