Как вы имитируете свойство только для чтения с помощью mock ?
Я старался:
setattr(obj.__class__, 'property_to_be_mocked', mock.Mock())
но проблема в том, что затем он применяется ко всем экземплярам класса ... что нарушает мои тесты.
Есть ли у вас другие идеи? Я не хочу высмеивать весь объект, только это конкретное свойство.
python
unit-testing
mocking
pytest
Charlax
источник
источник
@property
. Этот ответ сработал для меня, когда другой ответ (и другие ответы на многие другие вопросы) не помог.Собственно, ответ был (как обычно) в документации , просто я применял патч к экземпляру, а не к классу, когда следовал их примеру.
Вот как это сделать:
class MyClass: @property def last_transaction(self): # an expensive and complicated DB query here pass
В наборе тестов:
def test(): # Make sure you patch on MyClass, not on a MyClass instance, otherwise # you'll get an AttributeError, because mock is using settattr and # last_transaction is a readonly property so there's no setter. with mock.patch(MyClass, 'last_transaction') as mock_last_transaction: mock_last_transaction.__get__ = mock.Mock(return_value=Transaction()) myclass = MyClass() print myclass.last_transaction
источник
mock.PropertyMock
способ сделать это!PropertyMock
не существовало.Если объект, свойство которого вы хотите переопределить, является фиктивным объектом, вам не нужно использовать
patch
.Вместо этого можно создать,
PropertyMock
а затем переопределить свойство для типа макета. Например, чтобы переопределитьmock_rows.pages
возвращаемое свойство(mock_page, mock_page,)
:mock_page = mock.create_autospec(reader.ReadRowsPage) # TODO: set up mock_page. mock_pages = mock.PropertyMock(return_value=(mock_page, mock_page,)) type(mock_rows).pages = mock_pages
источник
Вероятно, дело в стиле, но если вы предпочитаете декораторы в тестах, ответ @ jamescastlefield можно изменить на что-то вроде этого:
class MyClass: @property def last_transaction(self): # an expensive and complicated DB query here pass class Test(unittest.TestCase): @mock.patch('MyClass.last_transaction', new_callable=PropertyMock) def test(self, mock_last_transaction): mock_last_transaction.return_value = Transaction() myclass = MyClass() print myclass.last_transaction mock_last_transaction.assert_called_once_with()
источник
В случае, если вы используете
pytest
вместеpytest-mock
, вы можете упростить свой код, а также избежать использования диспетчера контекста, т. Е. Следующегоwith
оператора:def test_name(mocker): # mocker is a fixture included in pytest-mock mocked_property = mocker.patch( 'MyClass.property_to_be_mocked', new_callable=mocker.PropertyMock, return_value='any desired value' ) o = MyClass() print(o.property_to_be_mocked) # this will print: any desired value mocked_property.assert_called_once_with()
источник
Если вы не хотите проверять, был ли получен доступ к фиктивному свойству, вы можете просто исправить его с помощью ожидаемого
return_value
.with mock.patch(MyClass, 'last_transaction', Transaction()): ...
источник
Если вам нужно, чтобы ваш макет
@property
полагался на оригинал__get__
, вы можете создать свой собственныйMockProperty
class PropertyMock(mock.Mock): def __get__(self, obj, obj_type=None): return self(obj, obj_type)
Применение:
class A: @property def f(self): return 123 original_get = A.f.__get__ def new_get(self, obj_type=None): return f'mocked result: {original_get(self, obj_type)}' with mock.patch('__main__.A.f', new_callable=PropertyMock) as mock_foo: mock_foo.side_effect = new_get print(A().f) # mocked result: 123 print(mock_foo.call_count) # 1
источник