Jak wyśmiewać właściwość tylko do odczytu za pomocą makiety ?
Próbowałem:
setattr(obj.__class__, 'property_to_be_mocked', mock.Mock())
ale problem polega na tym, że dotyczy to wszystkich instancji klasy ... co psuje moje testy.
Masz inny pomysł? Nie chcę kpić z całego obiektu, tylko z tej konkretnej właściwości.
python
unit-testing
mocking
pytest
charlax
źródło
źródło
@property
. Ta odpowiedź zadziałała dla mnie, gdy inna odpowiedź (i inne odpowiedzi na wiele innych pytań) nie zadziałała.Właściwie odpowiedź brzmiała (jak zwykle) w dokumentacji , po prostu zastosowałem łatkę do instancji zamiast do klasy, kiedy poszedłem za ich przykładem.
Oto jak to zrobić:
class MyClass: @property def last_transaction(self): # an expensive and complicated DB query here pass
W zestawie testowym:
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
źródło
mock.PropertyMock
jest na to sposób!PropertyMock
nie było.Jeśli obiekt, którego właściwość chcesz przesłonić, jest obiektem pozorowanym, nie musisz używać
patch
.Zamiast tego można utworzyć,
PropertyMock
a następnie zastąpić właściwość typu makiety. Na przykład, aby przesłonićmock_rows.pages
właściwość do zwrócenia(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
źródło
Pewnie to kwestia stylu, ale w przypadku wolisz dekoratorów w testach użytkownika @ jamescastlefield odpowiedź może być zmieniony na coś takiego:
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()
źródło
Jeśli używasz
pytest
razem zpytest-mock
, możesz uprościć swój kod, a także uniknąć używania menedżera kontekstu, tj.with
Instrukcji w następujący sposób: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()
źródło
Jeśli nie chcesz testować, czy uzyskano dostęp do fałszywej właściwości, możesz po prostu załatać ją za pomocą oczekiwanego
return_value
.with mock.patch(MyClass, 'last_transaction', Transaction()): ...
źródło
Jeśli chcesz, aby twój wyśmienity
@property
polegał na oryginale__get__
, możesz stworzyć swój własnyMockProperty
class PropertyMock(mock.Mock): def __get__(self, obj, obj_type=None): return self(obj, obj_type)
Stosowanie:
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
źródło