如何使用装饰器修补sys属性?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用装饰器修补sys属性?相关的知识,希望对你有一定的参考价值。

我有一个依赖于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()

TypeError: '<' not supported between instances of 'MagicMock' and 'int'试图做my_func时失败与if sys.version_info[0] < 3

答案

Edit:

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

Edit2:

正如Zaur Nasibov所指出的那样sys.version_info不是一个函数,所以我的代码错了尽管看起来很好的模拟(如GenError也发现)。我做了一些修改来修复它(使用PropertyMock查看GenError的替代方案)

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.
另一答案

我找到了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

因此,您必须模拟在模块a中导入的sys模块,并在其上设置PropertyMock

以上是关于如何使用装饰器修补sys属性?的主要内容,如果未能解决你的问题,请参考以下文章

我可以在包装函数之前修补 Python 装饰器吗?

python使用上下文对代码片段进行计时,非装饰器

python进阶之装饰器之2.定义一个可接受参数的装饰器如何定义一个属性可由用户修改的装饰器定义一个能接受可选参数的装饰器

装饰器

如何在 Python 的方法装饰器中使用类属性?

学习笔记2-functools.wraps 装饰器