web-dev-qa-db-fra.com

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

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

J'essaie d'écrire des tests unitaires autour de cette fonction foo, en moquant l'appel à bar(). Malheureusement, je suis incapable d'obtenir l'appel simulé de bar() pour déclencher une exception qui est capturée par mon bloc except.

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 le Mock Docs qui indique que vous devez définir le side_effect d'une instance Mock sur une classe Exception pour que la fonction simulée génère l'erreur.

J'ai également consulté d'autres questions-réponses sur StackOverflow, et il semble que je fasse la même chose que ce qu'ils font pour faire en sorte qu'Exception soit élevé par leur maquette.

Pourquoi régler le side_effect de barMock ne provoque pas l'augmentation du Exception attendu? Si je fais quelque chose de bizarre, comment dois-je procéder pour tester la logique dans mon bloc except?

89
Jesse Webb

Votre modèle soulève l'exception très bien, mais la valeur error.resp.status est manquante. Plutôt que d'utiliser return_value, dites simplement à Mock que status est un attribut:

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

Des arguments de mot clé supplémentaires pour Mock() sont définis en tant qu'attributs sur l'objet résultant.

Je mets vos définitions foo et bar dans un module my_tests, ajoutées à la classe HttpError afin que je puisse l'utiliser aussi. peut être couru vers le 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 ligne print '404 - %s' % error.message courir, mais je pense que vous vouliez utiliser error.content à la place; c'est l'attribut HttpError() définit à partir du deuxième argument, en tout cas.

109
Martijn Pieters