Wie man Benutzer und Anfragen in Django verspottet

75

Ich habe Django-Code, der mit Anforderungsobjekten oder Benutzerobjekten interagiert. Zum Beispiel so etwas wie:

foo_model_instance = models.get_or_create_foo_from_user(request.user)

Wenn Sie mit der Django-Python-Shell oder in einem Unittest testen würden, was würden Sie dort weitergeben? Hier reicht einfach ein Benutzerobjekt aus, aber die Notwendigkeit eines Scheinanforderungsobjekts tritt auch häufig auf.

Für die Muschel oder für Unittests:

  • Wie verspotten Sie Benutzer?
  • Wie verspotten Sie Anfragen?
Purrell
quelle
2
"Sie verwenden dieses Wort weiterhin, aber ich glaube nicht, dass es bedeutet, was Sie denken, dass es bedeutet ..." Ich denke, Sie meinen "Modell".
Mike DeSimone
9
@ Mike: Es klingt lustig, aber ich denke, er hat es richtig gemacht. @pax: Schlagen Sie mich bis zur Pointe :(
mpen
6
Ich ... ich muss gestehen, dass ... in der Stille meines Zimmers ... spät in der Nacht ... ich ... ja, ja! Ich verspotte Benutzer! Alle von ihnen! @perrierism: Wir machen uns nicht über dich lustig, wir genießen einfach deine wundervolle Wortwahl.
Peter Rowell

Antworten:

84

Für eine Anfrage würde ich RequestFactory verwenden, das in Django enthalten ist.

from django.test.client import RequestFactory
rf = RequestFactory()
get_request = rf.get('/hello/')
post_request = rf.post('/submit/', {'foo': 'bar'})

Für Benutzer würde ich django.contrib.auth.models.User verwenden, wie von @ozan vorgeschlagen, und möglicherweise mit Factory Boy für Geschwindigkeit (mit Factory Boy können Sie wählen, nicht in DB zu speichern)

Naoko
quelle
1
Dies ist definitiv die richtige Antwort, sobald RequestFactory verfügbar ist. Mit Anerkennung für Ozans Antwort (dass das Instanziieren der realen Objekte ausreichend und wünschenswert ist). Ich habe Factory Boy nicht verwendet, aber wenn es der Qualität von Rail's Factory Girl nahe kommt, scheint es eine ausgezeichnete Wahl zu sein.
Purrell
Beste Lösung, die für mich in einem Django-Migrationsskript funktioniert hat. Die aktuelle Version muss request.userjedoch eingestellt werden. Auch um diese Anfrage wie eine normale Ansicht zu verwenden, ist es schön, sie request.csrf_processing_done = Truein der fertigen Anfrage zu haben (um die CSRF-Prüfungen zu bestehen)
garmoncheg
50

Wie verspotten Sie Benutzer?

Initialisieren Sie ein django.contrib.auth.models.UserObjekt. User.objects.create_usermacht das einfach.

Wie verspotten Sie Anfragen?

Initialisieren Sie ein django.http.HttpRequestObjekt.

Natürlich gibt es Verknüpfungen, je nachdem, was Sie tun möchten. Wenn Sie nur ein Objekt mit einem userAttribut benötigen , das auf einen Benutzer verweist, erstellen Sie einfach etwas (irgendetwas) und geben Sie ihm dieses Attribut.

Ozan
quelle
2
@ S.Lott es ist gut, manchmal die reale Sache zu verwenden, aber es endet sehr langsam, wenn Ihr Projekt wächst. Es ist schön, Mock-Tests zu haben, die Sie in wenigen Sekunden anstatt in wenigen Minuten ausführen können.
TM.
1
@ TM: Vielleicht ist das im Allgemeinen wahr. Aber der Django-Client ist sehr schnell. Haben Sie eine Alternative und einige Benchmarks, um die Zeitersparnis aufzuzeigen?
S.Lott
@ S.Lott nur Erfahrung mit meinen eigenen Projekten. Eine Alternative zur Verwendung des integrierten Benutzers besteht darin, nur das Pymox-Framework zu verwenden oder eigene Scheinobjekte mit derselben API zu erstellen, wie in einigen Antworten hier vorgeschlagen. Die Verwendung realer Anforderungsobjekte ist kein Geschwindigkeitsproblem.
TM.
11
@S. Ich sage, sie sind schneller als der DB-Zugriff (was nur für den UserTeil dieser Frage gilt). Wenn Sie nicht glauben, dass Mocks schneller sind als der tatsächliche Zugriff auf die Datenbank, dann haben Sie vermutlich nicht wirklich versucht und verglichen. Bei einem sehr kleinen Projekt wechselte unser Team von der Testsuite, die 1,5 Minuten bis <5 Sekunden dauerte, als wir den Code änderten, um Modelle zu verspotten. Es ist nicht nötig, einen Benchmark zu schreiben, wenn die Lücke so groß ist.
TM.
1
Meine wirklichen Anfragen haben ein .userAttribut. Instanzen von django.http.HttpRequestnicht. Ich setze gerade request.user, nachdem ich es erstellt habe. Scheint das vernünftig?
Jonathan Hartley
7

Sie können entweder Ihre eigenen Mocks würfeln, wie Anurag Uniyal vorgeschlagen hat, oder Sie können ein spöttisches Framework verwenden.

Als Antwort auf die Aussage, dass Sie einen normalen Benutzer wie in Django erstellen können, würde ich vorschlagen, dass dies den Punkt des Komponententests zunichte macht. Ein Komponententest sollte die Datenbank nicht berühren, aber durch das Erstellen eines Benutzers haben Sie die Datenbank geändert, weshalb wir eine verspotten möchten.

Michael Williamson
quelle
2
Wie Daniel erwähnt hat, erstellt der Testläufer dann eine Testdatenbank für Sie, sodass Sie sich darüber keine Sorgen machen müssen.
Ozan
4
Außer wenn Sie die Datenbank verwenden, handelt es sich nicht mehr um einen Komponententest. Es ist zwar immer noch ein vollkommen gültiger Integrationstest, aber kein Komponententest.
Michael Williamson
1
Das Problem ist, dass das Erstellen und Zerstören einer Datenbank einige Zeit in Anspruch nimmt. Ich möchte jedes Mal, wenn ich etwas ändere, im Handumdrehen Tausende von Tests durchführen. Ich möchte keine Datenbank und keine Anwendungsinstanz einrichten, um meine Tests auszuführen.
Tim Ottinger
Einverstanden ist es sehr nützlich, Tests zu haben, die keine Datenbank treffen. Selbst wenn Sie eine SQLite-Datenbank verwenden, ist diese immer noch langsamer als Tests, die Mocks verwenden.
TM.
6

Lesen Sie hier über Scheinobjekte
http://en.wikipedia.org/wiki/Mock_object
http://www.mockobjects.com/

Und verwenden Sie diese Python-Bibliothek, um einen Benutzer zu verspotten
http://python-mock.sourceforge.net/

Andernfalls können Sie selbst eine einfache Benutzerklasse schreiben. Verwenden Sie diese als Ausgangspunkt

class MockUser(object):
    def __call__(self, *args, **kwargs):
        return self

    def __getattr__(Self, name):
        return self

Fügen Sie spezielle Fälle usw. usw. hinzu

Anurag Uniyal
quelle
5

Sie müssen Benutzer nicht verspotten, da Sie nur einen in Ihrem Test erstellen können - die Datenbank wird nach Abschluss des Tests zerstört.

Verwenden Sie dieses Snippet von Simon Willison, um Anfragen zu verspotten .

Daniel Roseman
quelle
Obwohl ich damit einverstanden bin, können Sie normalerweise Benutzer für Ihre Tests erstellen - es gibt Zeiten, in denen Sie dies nicht möchten - oder dies liegt außerhalb des Aufgabenbereichs des Tests. Wenn ich teste, dass diese Berechtigung verweigert wird, wenn eine Hilfsmethode False zurückgibt, ist es falsch, dass ich das mit der Datenbank kopple. Diese Hilfsmethode existiert, weil ich nichts über die Datenbankdarstellung wissen möchte.
Yarbelk