我正在尝试模拟一个属性,并希望根据对象中的其他状态来控制该属性的返回值。 一个有代表性的小例子如下所示
import datetime
from pytest_mock import MockFixture
class MyObject:
def __init__(self, name: str):
self._name = name
self._creation_time = datetime.datetime.now()
@property
def creation_time(self) -> datetime.datetime:
# A series of
# calculations
# here
# return_value = ... something
return return_value
def test_ordered_creation_time(mocker: MockFixture) -> None:
def creation_time_side_effect(self) -> datetime.datetime:
if self._name == 'first_created':
return datetime.datetime(2020, 1, 1)
elif self._name == 'second_created':
return datetime.datetime(2020, 2, 1)
mock_creation_time = mocker.patch.object(
MyObject,
'creation_time',
new_callable=mocker.PropertyMock)
mock_creation_time.side_effect = creation_time_side_effect
first_created = MyObject('first_created')
second_created = MyObject('second_created')
assert first_created.creation_time < second_created.creation_time
用 pytest 运行它给了我
E TypeError: test_ordered_creation_time.<locals>.creation_time_side_effect() missing 1 required positional argument: 'self'
看起来在使用 PropertyMock 并设置 side_effect 函数时,该函数无法访问
self
对象。
那是对的吗 ?如果是,那是为什么?
或者,是否有另一种方法可以在模拟属性时检查
self
对象
pytest 的完整错误
====================================================================================================================================================================================== FAILURES ======================================================================================================================================================================================
_____________________________________________________________________________________________________________________________________________________________________________ test_ordered_creation_time _____________________________________________________________________________________________________________________________________________________________________________
mocker = <pytest_mock.plugin.MockerFixture object at 0x103fb1210>
def test_ordered_creation_time(mocker: MockFixture) -> None:
def creation_time_side_effect(self) -> datetime.datetime:
if self._name == 'first_created':
return datetime.datetime(2020, 1, 1)
elif self._name == 'second_created':
return datetime.datetime(2020, 2, 1)
mock_creation_time = mocker.patch.object(
MyObject,
'creation_time',
new_callable=mocker.PropertyMock)
mock_creation_time.side_effect = creation_time_side_effect
first_created = MyObject('first_created')
second_created = MyObject('second_created')
> assert first_created.creation_time < second_created.creation_time
dummy.py:38:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/opt/homebrew/Cellar/[email protected]/3.11.4/Frameworks/Python.framework/Versions/3.11/lib/python3.11/unittest/mock.py:2946: in __get__
return self()
/opt/homebrew/Cellar/[email protected]/3.11.4/Frameworks/Python.framework/Versions/3.11/lib/python3.11/unittest/mock.py:1124: in __call__
return self._mock_call(*args, **kwargs)
/opt/homebrew/Cellar/[email protected]/3.11.4/Frameworks/Python.framework/Versions/3.11/lib/python3.11/unittest/mock.py:1128: in _mock_call
return self._execute_mock_call(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <PropertyMock name='creation_time' id='4361753680'>, args = (), kwargs = {}, effect = <function test_ordered_creation_time.<locals>.creation_time_side_effect at 0x103f9e3e0>
def _execute_mock_call(self, /, *args, **kwargs):
# separate from _increment_mock_call so that awaited functions are
# executed separately from their call, also AsyncMock overrides this method
effect = self.side_effect
if effect is not None:
if _is_exception(effect):
raise effect
elif not _callable(effect):
result = next(effect)
if _is_exception(result):
raise result
else:
> result = effect(*args, **kwargs)
E TypeError: test_ordered_creation_time.<locals>.creation_time_side_effect() missing 1 required positional argument: 'self'
/opt/homebrew/Cellar/[email protected]/3.11.4/Frameworks/Python.framework/Versions/3.11/lib/python3.11/unittest/mock.py:1189: TypeError
只需使用
PropertyMock
+ side_effect
。这是一个例子:
# run.py
from unittest.mock import PropertyMock, patch
from datetime import datetime
class MyObject:
@property
def creation_time(self) -> datetime:
return datetime.utcnow()
def test_ordered_creation_time() -> None:
with patch(
# change run to your module...
'run.MyObject.creation_time',
new_callable=PropertyMock,
side_effect=[datetime(2020, 1, 1), datetime(2020, 1, 2)]
):
assert MyObject().creation_time < MyObject().creation_time
#============================= test session starts #==============================
#collecting ... collected 1 item
#
#run.py::test_ordered_creation_time PASSED [100%]
#
#============================== 1 passed in 2.61s #===============================