Se moquer d'une fonction pour lever une exception pour tester un bloc except


119

J'ai une fonction ( foo) qui appelle une autre fonction ( bar). Si l'invocation bar()déclenche un HttpError, je veux le gérer spécialement si le code d'état est 404, sinon re-relancer.

J'essaie d'écrire des tests unitaires autour de cette foofonction, en se moquant de l'appel à bar(). Malheureusement, je ne peux pas obtenir l'appel simulé à bar()pour lever une exception qui est interceptée par mon exceptbloc.

Voici mon code qui illustre mon problème:

import unittest
import mock
from apiclient.errors import HttpError


class FooTests(unittest.TestCase):
    @mock.patch('my_tests.bar')
    def test_foo_shouldReturnResultOfBar_whenBarSucceeds(self, barMock):
        barMock.return_value = True
        result = foo()
        self.assertTrue(result)  # passes

    @mock.patch('my_tests.bar')
    def test_foo_shouldReturnNone_whenBarRaiseHttpError404(self, barMock):
        barMock.side_effect = HttpError(mock.Mock(return_value={'status': 404}), 'not found')
        result = foo()
        self.assertIsNone(result)  # fails, test raises HttpError

    @mock.patch('my_tests.bar')
    def test_foo_shouldRaiseHttpError_whenBarRaiseHttpErrorNot404(self, barMock):
        barMock.side_effect = HttpError(mock.Mock(return_value={'status': 500}), 'error')
        with self.assertRaises(HttpError):  # passes
            foo()

def foo():
    try:
        result = bar()
        return result
    except HttpError as error:
        if error.resp.status == 404:
            print '404 - %s' % error.message
            return None
        raise

def bar():
    raise NotImplementedError()

J'ai suivi les documents Mock qui disent que vous devez définir le side_effectd'une Mockinstance sur une Exceptionclasse pour que la fonction simulée lève l'erreur.

J'ai également regardé d'autres questions et réponses liées à StackOverflow, et il semble que je fais la même chose qu'ils font pour provoquer et déclencher une exception par leur simulation.

Pourquoi le réglage side_effectde barMockne provoque-t-il pas le relèvement de l'attendu Exception? Si je fais quelque chose de bizarre, comment dois-je procéder pour tester la logique de mon exceptbloc?


Je suis presque sûr que votre exception est soulevée, mais je ne sais pas comment vous définissez le resp.statuscode ici. D'où HTTPErrorvient-il?
Martijn Pieters

@MartijnPieters HttpErrorest une classe définie dans Google apiclientlib que nous utilisons dans GAE. Il __init__est défini avec les paramètres, (resp, content)donc j'essayais de créer une instance fictive pour la réponse, avec le code d'état approprié spécifié.
Jesse Webb

Bon, c'est donc cette classe ; mais vous n'avez pas besoin d'utiliser return_valuealors; respn'est pas appelé .
Martijn Pieters

1
J'ai réessayé mon code sans utiliser HttpErroret j'ai essayé d'utiliser à la place une Exceptioninstance régulière . Cela fonctionne parfaitement. Cela signifie qu'il doit avoir quelque chose à voir avec la façon dont je configure l' HttpErrorinstance, probablement lié à la façon dont je crée une Mockinstance pour la réponse.
Jesse Webb

Réponses:


141

Votre simulation soulève très bien l'exception, mais la error.resp.statusvaleur est manquante. Plutôt que d'utiliser return_value, dites simplement Mockque statusc'est un attribut:

barMock.side_effect = HttpError(mock.Mock(status=404), 'not found')

Les arguments de mot-clé supplémentaires à Mock()sont définis comme attributs sur l'objet résultant.

J'ai mis vos définitions fooet bardans un my_testsmodule, ajouté dans la HttpErrorclasse pour que je puisse l'utiliser aussi, et votre test peut ensuite être exécuté avec succès:

>>> from my_tests import foo, HttpError
>>> import mock
>>> with mock.patch('my_tests.bar') as barMock:
...     barMock.side_effect = HttpError(mock.Mock(status=404), 'not found')
...     result = my_test.foo()
... 
404 - 
>>> result is None
True

Vous pouvez même voir la print '404 - %s' % error.messageligne courir, mais je pense que vous vouliez l'utiliser à la error.contentplace; c'est en tout cas les HttpError()ensembles d' attributs du deuxième argument.


2
la side_effectest la partie clé
Daniel Butler
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.