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
?
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.