2013-06-03 7 views
9

Ich habe ein dorniges Problem, das ich nicht in den Griff bekommen kann. Ich bin schreibe derzeit Komponententests für ein django benutzerdefinierte Auth-Backend. Auf unserem System haben wir eigentlich zwei Backends: ein eingebautes Django-Backend und das benutzerdefinierte Backend, das Anfragen an eine Java-basierte API sendet , die Benutzerinformationen in Form von XML zurückgibt. Jetzt schreibe ich Unit Tests, so dass ich keine Anfragen außerhalb des Systems wie senden möchte, dass ich nicht versuche, die Java-API zu testen, so meine Frage ist, wie kann ich umgehen und das Mock das Nebenwirkungen auf die robusteste Weise.Saubere Mocking-Remote-Server und APIs für Django-Unit-Tests

Die Funktion I-Tests sind, ist so etwas wie diese, wo die URL Einstellungen Wert nur die Basis-URL für den Java-Server, der die Benutzername und Passwort-Daten und gibt die xml authentifiziert, und der Service Wert ist nur einig Magie für den Aufbau der uRL-Abfrage, seine unwichtig für uns:

@staticmethod 
def get_info_from_api_with_un_pw(username, password, service=12345): 
    url = settings.AUTHENTICATE_URL_VIA_PASSWORD 
    if AUTH_FIELD == "username": 
     params = {"nick": username, "password": password} 
    elif AUTH_FIELD == "email": 
     params = {"email": username, "password": password} 
    params["service"] = service 
    encoded_params = urlencode([(k, smart_str(v, "latin1")) for k, v in params.items()]) 
    try: 
     # get the user's data from the api 
     xml = urlopen(url + encoded_params).read() 
     userinfo = dict((e.tag, smart_unicode(e.text, strings_only=True)) 
         for e in ET.fromstring(xml).getchildren()) 
     if "nil" in userinfo: 
      return userinfo 
     else: 
      return None 

wir die xml So erhalten, es in ein dict analysieren und, wenn der Schlüssel null vorhanden ist, dann können wir die dict und weitermachen glücklich zurückkehren und authentifiziert. Anschaulich wird eine Lösung nur einen Weg zu finden, irgendwie außer Kraft zu setzen oder monkeypatch die Logik in dem XML-Variable, ich diese Antwort gefunden:

How can one mock/stub python module like urllib

Ich habe versucht, so etwas zu implementieren, aber die Details dort sind sehr skizzenhaft und ich konnte nicht scheinen, dass das funktioniert.

I erfaßt auch die XML-Antwort und stecke es in einer lokalen Datei im Testordner mit der Absicht, einen Weg zu finden, dass als Mock Reaktion zu verwenden, die in die URL-Parameter der Testfunktion übergeben wird, so etwas wie dies außer Kraft setzen Sie die uRL:

@override_settings(AUTHENTICATE_URL_VIA_PASSWORD=(os.path.join(os.path.dirname(__file__), "{0}".format("response.xml")))) 
def test_get_user_info_username(self): 
    self.backend = RemoteAuthBackend() 
    self.backend.get_info_from_api_with_un_pw("user", "pass") 

Aber das muss auch wegen der uRL Gebäude Logik nehmen, die die Funktion definiert, (dh „url + encoded_params“). Wieder könnte ich die Antwortdatei umbenennen, um die gleiche wie die verkettete URL zu sein, aber das wird weniger wie ein guter Unit-Test für die Funktion und mehr von einem "Betrüger", das Ganze Ding wird gerade mehr und mehr spröde die ganze Zeit mit diesen Lösungen, und es ist wirklich nur eine Vorrichtung sowieso, die ich auch vermeiden möchte, wenn überhaupt möglich ist.

Ich fragte mich auch, ob es eine Möglichkeit geben könnte, die XML auf dem Django-Entwicklungsserver zu liefern und dann die Funktion darauf zu richten? Es scheint eine vernünftige Lösung zu sein, aber viel Googeln gab mir keine Hinweise, ob so etwas möglich oder ratsam wäre, und selbst dann denke ich nicht, dass dies ein Test wäre, der außerhalb der Entwicklungsumgebung laufen würde.

Also, im Idealfall, ich brauche die Lage sein, irgendwie einen „Server“ zu im Funktionsaufruf an die Stelle des Java-API, um zu verspotten, oder irgendwie einig xml Nutzlast dienen up, dass die Funktion als URL öffnen oder Monkeypatch die Funktion aus dem Test selbst, oder ...

Hat die Mock-Bibliothek die entsprechenden Tools, um solche Dinge zu tun?

http://www.voidspace.org.uk/python/mock

So gibt es zwei Punkte auf diese Frage 1) Ich mag würde mein besonderes Problem auf saubere Weise, und was noch wichtiger ist 2), was ist die besten Praktiken lösen für Django Einheit sauber schreiben -Tests, wenn Sie abhängig von Daten, Cookies usw. für die Benutzerauthentifizierung von einer Remote-API sind, die außerhalb Ihrer Domain ist?

Antwort

1

Die Scheinbibliothek sollte funktionieren, wenn sie ordnungsgemäß verwendet wird. Ich bevorzuge die minimock Bibliothek und ich schrieb eine kleine Basiseinheit Testfall (minimocktest), die dabei hilft.

Wenn Sie diesen Testfall mit Django integrieren zu testen urllib Sie es tun können, wie folgt:

from minimocktest import MockTestCase 
from django.test import TestCase 
from django.test.client import Client 

class DjangoTestCase(TestCase, MockTestCase): 
    ''' 
    A TestCase class that combines minimocktest and django.test.TestCase 
    ''' 

    def _pre_setup(self): 
     MockTestCase.setUp(self) 
     TestCase._pre_setup(self) 
     # optional: shortcut client handle for quick testing 
     self.client = Client() 

    def _post_teardown(self): 
     TestCase._post_teardown(self) 
     MockTestCase.tearDown(self) 

Jetzt können Sie diesen Testfall verwenden Sie stattdessen den Django-Test bei Verwendung direkt:

class MySimpleTestCase(DjangoTestCase): 
    def setUp(self): 
     self.file = StringIO.StringIO('MiniMockTest') 
     self.file.close = self.Mock('file_close_function') 
    def test_urldump_dumpsContentProperly(self): 
     self.mock('urllib2.urlopen', returns=self.file) 
     self.assertEquals(urldump('http://pykler.github.com'), 'MiniMockTest') 
     self.assertSameTrace('\n'.join([ 
      "Called urllib2.urlopen('http://pykler.github.com')", 
      "Called file_close_function()", 
     ])) 
     urllib2.urlopen('anything') 
     self.mock('urllib2.urlopen', returns=self.file, tracker=None) 
     urllib2.urlopen('this is not tracked') 
     self.assertTrace("Called urllib2.urlopen('anything')") 
     self.assertTrace("Called urllib2.urlopen('this is mocked but not tracked')", includes=False) 
     self.assertSameTrace('\n'.join([ 
      "Called urllib2.urlopen('http://pykler.github.com')", 
      "Called file_close_function()", 
      "Called urllib2.urlopen('anything')", 
     ])) 
+0

In Ihrem Fall werden Sie sich über Folgendes lustig machen: 'Import-Modelle; self.mock ('models.urlopen') 'da es scheint, dass Sie es wie folgt in Ihre Modelldatei importiert haben:' from urllib import urlopen'. – Pykler

+1

Ah, das ist großartig, sieht aus wie solche Spott ist nur das Ticket. Vielen Dank! – osman

0

Hier sind die Grundlagen der Lösung, die ich für die Aufzeichnung beendet habe. Ich habe am Ende die Mock-Bibliothek selbst statt Mockito benutzt, aber die Idee ist die gleiche: