134 votes

Comment simuler une propriété en lecture seule avec mock ?

Comment faire pour simuler une propriété en lecture seule avec simuler ?

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.

Vous avez une autre idée ? Je ne veux pas simuler l'objet complet, seulement cette propriété spécifique.

240voto

jamescastlefield Points 2246

Je pense que la meilleure façon de procéder est de simuler la propriété en tant que PropertyMock plutôt que de se moquer de la __get__ directement.

Il est indiqué dans le documentation chercher unittest.mock.PropertyMock : Un mock destiné à être utilisé comme une propriété, ou autre descripteur, sur une classe. PropertyMock fournit __get__ y __set__ afin que vous puissiez spécifier une valeur de retour lorsqu'il est récupéré.

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()

47voto

charlax Points 3653

En fait, la réponse était (comme d'habitude) dans le documentation C'est juste que j'ai appliqué le patch à l'instance au lieu de la classe lorsque j'ai suivi leur exemple.

Voici comment procéder :

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

18voto

Tim Swast Points 3697

Si l'objet dont vous voulez surcharger la propriété est un objet fantaisie, vous n'avez pas besoin d'utiliser la fonction patch .

Au lieu de cela, on peut créer un PropertyMock et ensuite remplacer la propriété sur le type du simulacre. Par exemple, pour remplacer mock_rows.pages pour retourner la propriété (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

14voto

lmiguelvargasf Points 9693

Si vous utilisez pytest ainsi que pytest-mock vous pouvez simplifier votre code et éviter d'utiliser le gestionnaire de contexte, c'est-à-dire la fonction with 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()

12voto

eyalev Points 60

C'est sans doute une question de style, mais si vous préférez les décorateurs dans les tests, l'article de @jamescastlefield réponse 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(self, mock_last_transaction):
        mock_last_transaction.return_value = Transaction()
        myclass = MyClass()
        print myclass.last_transaction
        mock_last_transaction.assert_called_once_with()

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X