Как высмеивать свойство readonly с макетом?
Как вы издеваетесь над свойством readonly с mock?
Я пробовал:
setattr(obj.__class__, 'property_to_be_mocked', mock.Mock())
но проблема в том, что он применяется ко всем экземплярам класса..., который разбивает мои тесты.
Есть ли у вас другая идея? Я не хочу издеваться над полным объектом, только это конкретное свойство.
Ответы
Ответ 1
Я думаю, что лучший способ состоит в том, чтобы высмеять свойство как PropertyMock
, а не издеваться над методом __get__
.
В документации указано unittest.mock.PropertyMock
:
Макет, предназначенный для использования в качестве свойства или другого дескриптора для класса. PropertyMock
предоставляет методы __get__
и __set__
, поэтому вы можете указать возвращаемое значение при его извлечении.
Вот как:
class MyClass:
@property
def last_transaction(self):
# an expensive and complicated DB query here
pass
def test(unittest.TestCase):
with mock.patch('MyClass.last_transaction', new_callable=PropertyMock) as mock_last_transaction:
mock_last_transaction.return_value = Transaction()
myclass = MyClass()
print myclass.last_transaction
mock_last_transaction.assert_called_once_with()
Ответ 2
Собственно, ответ был (как обычно) в документации, просто я применил патч к экземпляру вместо класса когда я последовал их примеру.
Вот как это сделать:
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 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
Ответ 3
Наверное, вопрос стиля, но если вы предпочитаете декораторов в тестах, @jamescastlefield answer может быть изменен на что-то вроде этого:
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(mock_last_transaction):
mock_last_transaction.return_value = Transaction()
myclass = MyClass()
print myclass.last_transaction
mock_last_transaction.assert_called_once_with()
Ответ 4
Если вы не хотите проверять, был ли доступ к издеваемому свойству, вы можете просто его исправить с ожидаемым return_value
.
with mock.patch(MyClass, 'last_transaction', Transaction()):
...
Ответ 5
Если объект, свойство которого вы хотите переопределить, является фиктивным объектом, вам не нужно использовать 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