问题
I have a function which depends on the Python version. I would like to test this in a unittest by mocking sys.version
info. The following code works:
def test_python_version(self):
with patch("sys.version_info", [3]):
my_func()
with patch("sys.version_info", [2]):
my_func()
I would like to use a decorator, but the following code does not work. Why is this? How do I set the value of the MagicMock object that gets passed into my test? Thanks
@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()
Fails with TypeError: '<' not supported between instances of 'MagicMock' and 'int'
when my_func
tries to do if sys.version_info[0] < 3
.
回答1:
Edit:
Yes it works. The original function returns a named tuple with major
, minor
and micro
fields. You can mimic it by building your own named tuple. I just use a simple tuple as you access with int
index instead. Problem with your code is in the way you indexed with [0]
which was not right.
Edit2:
As Zaur Nasibov pointed out sys.version_info
is not a function, and so my code was wrong despite looking fine with the mocks (as GenError also found). I have done a little change to fix it (see GenError answer for an alternative using 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()
Outputs
Detected version: (3, 6, 2)
Detected version: (2, 7, 0)
Error: Python 3 required.
回答2:
I found the answer by progmatico but it has a serious issue for me, as it requires to call sys.version_info()
instead of sys.version_info
which would break the code if ran normally.
Example:
#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')
Now if I want to test both cases in a unit test @patch("sys.version_info")
will lead to an error as given by OP. Changing do_something()
to use sys.version_info()
would break it if the mock is not used.
Test file:
#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
So you have to mock the sys module imported in the module a and set a PropertyMock
on it.
来源:https://stackoverflow.com/questions/52928397/how-do-i-patch-a-sys-attribute-using-a-decorator