Verspotten einer Funktion zum Auslösen einer Ausnahme zum Testen eines Ausnahmeblocks

118

Ich habe eine Funktion ( foo), die eine andere Funktion ( bar) aufruft . Wenn das Aufrufen bar()eines ausgelöst wird HttpError, möchte ich es speziell behandeln, wenn der Statuscode 404 lautet, andernfalls erneut erhöhen.

Ich versuche, einige Unit-Tests um diese fooFunktion herum zu schreiben und den Aufruf von zu verspotten bar(). Leider kann ich den verspotteten Anruf nicht erhalten bar(), um eine Ausnahme auszulösen, die von meinem exceptBlock erfasst wird.

Hier ist mein Code, der mein Problem veranschaulicht:

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

Ich folgte den Mock docs , die sagen , dass Sie die eingestellte sollte side_effecteine MockInstanz in eine ExceptionKlasse , um die verspottete Funktion erhöhen , den Fehler zu haben.

Ich habe mir auch einige andere verwandte Fragen und Antworten zu StackOverflow angesehen, und es sieht so aus, als würde ich das Gleiche tun, um zu bewirken, dass die Ausnahme durch ihren Schein ausgelöst wird.

Warum setzt die side_effectvon barMocknicht der erwartete verursacht Exceptionerhöht werden? Wenn ich etwas Seltsames mache, wie soll ich dann die Logik in meinem exceptBlock testen ?

Jesse Webb
quelle
Ich bin mir ziemlich sicher , dass Ihre Ausnahme wird ausgelöst wird, aber ich bin mir nicht sicher , wie Sie die setzen resp.statusCode gibt. Woher kommt HTTPErrordas?
Martijn Pieters
@MartijnPieters HttpErrorist eine Klasse , in definierten Google apiclientlib , die wir in GAE verwenden. Es __init__wird mit den Parametern definiert, (resp, content)daher habe ich versucht, eine Scheininstanz für die Antwort mit dem entsprechenden angegebenen Statuscode zu erstellen.
Jesse Webb
Richtig, so ist es diese Klasse ; aber Sie müssen dann nicht Benutzer return_value; respwird nicht aufgerufen .
Martijn Pieters
1
Ich habe meinen Code erneut ohne Verwendung HttpErrorversucht und stattdessen nur eine reguläre ExceptionInstanz verwendet. Das funktioniert perfekt. Dies bedeutet, dass es etwas damit zu tun haben muss, wie ich die HttpErrorInstanz konfiguriere , wahrscheinlich im Zusammenhang damit, wie ich eine MockInstanz für die Antwort erstelle .
Jesse Webb

Antworten:

141

Ihr Mock löst die Ausnahme ein, aber der error.resp.statusWert fehlt. Anstatt Gebrauch return_value, nur sagen , Mockdass statusein Attribut:

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

Zusätzliche Schlüsselwortargumente, Mock()die als Attribute für das resultierende Objekt festgelegt werden sollen.

Ich habe Ihre foound barDefinitionen in ein my_testsModul eingefügt, das im hinzugefügt wurdeHttpError Klasse , damit ich es auch verwenden kann, und Ihr Test kann dann erfolgreich ausgeführt werden:

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

Sie können sogar sehen, wie die print '404 - %s' % error.messageLinie läuft, aber ich denke, Sie wollten sie error.contentstattdessen dort verwenden. das sind HttpError()jedenfalls die Attributmengen aus dem zweiten Argument.

Martijn Pieters
quelle
2
Das side_effectist der Schlüssel
Daniel Butler