Как мне исправить атрибут sys с помощью декоратора? - PullRequest
0 голосов
/ 22 октября 2018

У меня есть функция, которая зависит от версии Python.Я хотел бы проверить это в unittest, высмеивая 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.

Ответы [ 2 ]

0 голосов
/ 13 марта 2019

Я нашел ответ от 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

Таким образом, вам нужно смоделировать модуль sys, импортированный в модуль a, и установить для него PropertyMock.

0 голосов
/ 22 октября 2018

Редактировать:

Да, это работает.Исходная функция возвращает именованный кортеж с полями major, minor и micro.Вы можете имитировать это, создав собственный кортеж с именем.Я просто использую простой кортеж, когда вы получаете доступ с индексом int.Проблема с вашим кодом связана с тем, что вы проиндексировали [0], что было неправильно.

Edit2:

Как отметил Заур Насибов, 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.
...