可以在使用 side_effect 函数通过 PropertyMock 模拟属性时访问 `self` 对象吗?

问题描述 投票:0回答:1

我正在尝试模拟一个属性,并希望根据对象中的其他状态来控制该属性的返回值。 一个有代表性的小例子如下所示

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
python python-unittest python-mock
1个回答
0
投票

只需使用

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 #===============================
© www.soinside.com 2019 - 2024. All rights reserved.