Je pense que le meilleur moyen est de se moquer de la propriété PropertyMock
plutôt que de se moquer __get__
directement de la méthode.
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 des __set__
méthodes 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):
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()
@property
. Cette réponse a fonctionné pour moi alors que l'autre réponse (et d'autres réponses à de nombreuses autres questions) n'a pas fonctionné.En fait, la réponse était (comme d'habitude) dans la documentation , c'est juste que j'appliquais le correctif à l'instance au lieu de la classe lorsque 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
la source
mock.PropertyMock
est le moyen de le faire!PropertyMock
n'existait pas.Si l'objet dont vous souhaitez remplacer la propriété est un objet fictif, vous n'avez pas à l'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 lamock_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
la source
Probablement une question de style, mais au cas où vous préférez les décorateurs dans les tests, la réponse de @ jamescastlefield pourrait être changée 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(self, mock_last_transaction): mock_last_transaction.return_value = Transaction() myclass = MyClass() print myclass.last_transaction mock_last_transaction.assert_called_once_with()
la source
Dans le cas où vous utilisez
pytest
avecpytest-mock
, vous pouvez simplifier votre code et également éviter d'utiliser le gestionnaire de contexte, c'est-à-dire l'with
instruction comme suit: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()
la source
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 fichier attendu
return_value
.with mock.patch(MyClass, 'last_transaction', Transaction()): ...
la source
Si vous avez besoin que votre simulé
@property
s'appuie sur l'original__get__
, vous pouvez créer votre personnaliséMockProperty
class PropertyMock(mock.Mock): def __get__(self, obj, obj_type=None): return self(obj, obj_type)
Usage:
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
la source