¿Cómo se burla de una propiedad de solo lectura con simulacro ?
Lo intenté:
setattr(obj.__class__, 'property_to_be_mocked', mock.Mock())
pero el problema es que luego se aplica a todas las instancias de la clase ... lo que rompe mis pruebas.
¿Tiene usted alguna otra idea? No quiero burlarme del objeto completo, solo esta propiedad específica.
python
unit-testing
mocking
pytest
charlax
fuente
fuente
@property
. Esta respuesta funcionó para mí cuando la otra respuesta (y otras respuestas a muchas otras preguntas) no lo hizo.En realidad, la respuesta estaba (como de costumbre) en la documentación , es solo que estaba aplicando el parche a la instancia en lugar de a la clase cuando seguí su ejemplo.
He aquí cómo hacerlo:
class MyClass: @property def last_transaction(self): # an expensive and complicated DB query here pass
En la suite de pruebas:
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
fuente
mock.PropertyMock
es la forma de hacerlo!PropertyMock
este artículo no existía.Si el objeto cuya propiedad desea anular es un objeto simulado, no tiene que usar
patch
.En su lugar, puede crear un
PropertyMock
y luego anular la propiedad en el tipo de simulacro. Por ejemplo, para anular lamock_rows.pages
propiedad para devolver(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
fuente
Probablemente sea una cuestión de estilo, pero en caso de que prefiera decoradores en las pruebas, la respuesta de @ jamescastlefield podría cambiarse a algo como esto:
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()
fuente
En caso de que esté usando
pytest
junto conpytest-mock
, puede simplificar su código y también evitar usar el administrador de contexto, es decir, lawith
declaración de la siguiente manera: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()
fuente
Si no desea probar si se accedió o no a la propiedad simulada, simplemente puede parchearla con el archivo esperado
return_value
.with mock.patch(MyClass, 'last_transaction', Transaction()): ...
fuente
Si necesita su burla
@property
para confiar en el original__get__
, puede crear su personalizadoMockProperty
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
fuente