Como você simula uma propriedade somente leitura com simulação ?
Eu tentei:
setattr(obj.__class__, 'property_to_be_mocked', mock.Mock())
mas o problema é que ele se aplica a todas as instâncias da classe ... o que quebra meus testes.
Você tem alguma outra ideia? Não quero zombar do objeto completo, apenas desta propriedade específica.
python
unit-testing
mocking
pytest
charlax
fonte
fonte
@property
. Essa resposta funcionou para mim quando a outra resposta (e outras respostas em muitas outras perguntas) não.Na verdade, a resposta estava (como sempre) na documentação , é que eu estava aplicando o patch à instância em vez da classe quando segui o exemplo.
Veja como fazer:
class MyClass: @property def last_transaction(self): # an expensive and complicated DB query here pass
No conjunto de testes:
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
fonte
mock.PropertyMock
é a maneira de fazer isso!PropertyMock
não existia.Se o objeto cuja propriedade você deseja substituir for um objeto simulado, você não precisa usar
patch
.Em vez disso, pode criar um
PropertyMock
e, em seguida, substituir a propriedade no tipo de simulação. Por exemplo, para substituir amock_rows.pages
propriedade para retornar(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
fonte
Provavelmente uma questão de estilo, mas no caso de você preferir decoradores nos testes, a resposta de @ jamescastlefield pode ser alterada para algo assim:
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()
fonte
Caso esteja usando
pytest
junto compytest-mock
, você pode simplificar seu código e também evitar o uso do gerenciador de contexto, ou seja, awith
instrução da seguinte forma: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()
fonte
Se você precisa do seu mock
@property
para confiar no original__get__
, pode criar seuMockProperty
class PropertyMock(mock.Mock): def __get__(self, obj, obj_type=None): return self(obj, obj_type)
Uso:
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
fonte
Se você não quiser testar se a propriedade simulada foi acessada ou não, você pode simplesmente corrigi-la com o esperado
return_value
.with mock.patch(MyClass, 'last_transaction', Transaction()): ...
fonte