2015-02-03 9 views
42

Ich habe eine Funktion (foo), die eine andere Funktion aufruft (bar). Wenn bar() ausgelöst wird, wird ein HttpError ausgelöst, ich möchte es speziell behandeln, wenn der Statuscode 404 ist, andernfalls re-raise.Verspotten einer Funktion zum Auslösen einer Ausnahme zum Testen eines Ausnahmeblocks

Ich versuche einige Komponententests um diese foo Funktion zu schreiben, die den Anruf zu bar() spotten. Leider bin ich nicht in der Lage, den verspotteten Anruf an bar() zu bekommen, um eine Ausnahme auszulösen, die von meinem except Block gefangen wird.

Hier ist mein Code, der mein Problem zeigt:

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 die Mock docs gefolgt, die sagen, dass Sie die side_effect einer Mock-Instanz in eine Exception Klasse gesetzt sollte die verspottete Funktion erhöhen, den Fehler zu haben.

Ich schaute auch auf einige andere verwandte StackOverflow Q & As, und es sieht aus wie ich mache das gleiche, was sie tun, um zu verursachen und Exception von ihrem Schein erhoben werden.

Warum Setzen der side_effect von barMock nicht den erwarteten Exception verursacht erhöht werden? Wenn ich etwas Seltsames mache, wie soll ich in meinem except Block Logik testen?

+0

Ich bin mir ziemlich sicher, dass Ihre Exception * ausgelöst wird, aber ich bin mir nicht sicher, wie Sie den 'resp.status'-Code dort einstellen. Woher kommt 'HTTPError'? –

+0

@MartijnPieters 'HttpError' ist eine Klasse, die in [Googles' apiclient' lib] (https://developers.google.com/api-client-library/python/) definiert ist, die wir in GAE verwenden. Es ist '__init__' mit den Parametern' (resp, content) 'definiert, also habe ich versucht, eine Mock-Instanz für die Antwort zu erstellen, mit dem entsprechenden Status-Code angegeben. –

+0

Richtig, also ist es [diese Klasse] (https://github.com/google/google-api-python-client/blob/master/googleapiclient/errors.py#L35-L63); aber Sie müssen dann 'return_value' nicht verwenden; 'resp' wird nicht * genannt *. –

Antwort

54

Ihr Spott hebt die Ausnahme gut an, aber der Wert error.resp.status fehlt. Anstatt Verwendung return_value, sagen nur Mock dass status ist ein Attribut:

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

Weitere Schlüsselwort Argumente Mock() werden als Attribute auf das resultierende Objekt festgelegt.

ich Ihre foo und bar Definitionen in einem my_tests Modul hinzugefügt im HttpError class so ist es, ich könnte zu verwenden, und Ihr Test kann dann zum Erfolg ran:

>>> 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 die print '404 - %s' % error.message sehen line run, aber ich denke du wolltest stattdessen error.content dort verwenden; das ist das Attribut HttpError() setzt von dem zweiten Argument auf jeden Fall.

+0

Das hat den Trick gemacht, danke! –