Comment se moquer d'une propriété en lecture seule avec mock ?
J'ai essayé:
setattr(obj.__class__, 'property_to_be_mocked', mock.Mock())
mais le problème est qu'il s'applique alors à toutes les instances de la classe ... ce qui casse mes tests.
Avez-vous une autre idée? Je ne veux pas me moquer de l'objet complet, seulement de cette propriété spécifique.
Je pense que la meilleure façon est de se moquer de la propriété en tant que PropertyMock
, plutôt que de se moquer de la __get__
méthode directement.
Il est indiqué dans la documentation , recherchez unittest.mock.PropertyMock
: Une maquette destinée à être utilisée comme propriété ou autre descripteur sur une classe. PropertyMock
fournit __get__
et __set__
afin que vous puissiez spécifier une valeur de retour lors de sa récupération.
Voici comment:
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()
En fait, la réponse était (comme d'habitude) dans le documentation , c'est juste que j'appliquais le patch à l'instance au lieu de la classe quand j'ai suivi leur exemple.
Voici comment faire:
class MyClass:
@property
def last_transaction(self):
# an expensive and complicated DB query here
pass
Dans la suite de tests:
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
Probablement une question de style mais si vous préférez les décorateurs dans les tests, @ jamescastlefield's answer pourrait être changé en quelque chose comme ceci:
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()
Si l'objet dont vous souhaitez remplacer la propriété est un objet factice, vous n'avez pas besoin d'utiliser patch
.
Au lieu de cela, peut créer un PropertyMock
puis remplacer la propriété sur le type de la maquette. Par exemple, pour remplacer mock_rows.pages
propriété à renvoyer (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
Si vous ne voulez pas tester si la propriété simulée a été accédée ou non, vous pouvez simplement la patcher avec le return_value
Attendu.
with mock.patch(MyClass, 'last_transaction', Transaction()):
...