2013-03-27 13 views
6

Ich verwende Django REST Framework für eine API, die ich arbeite. Aus verschiedenen Gründen möchte ich klassenbasierte Ansichten verwenden. Ich bin jedoch ein wenig speziell in Bezug auf meine Gerätetests, und ich erlaube meinen Gerätetests niemals, die Datenbank zu berühren. Hinweis: Ich verwende immer den "Trick", den Carl Meyer auf PYCON 2012 vorführt, wo er die Cursor-Hülle ausspioniert.Mocking-Funktionen in Django klassenbasierten Ansichten

Hier ist link wenn Sie an der Folie interessiert sind.

Ich habe eine Methode in einer der Ansichten, die etwas in der Datenbank überprüft. Um DRY zu sein, wird es zwischen einem POST und einem PUT geteilt. Aber, ich habe Probleme, es für meinen Komponententest zu verspotten. Das liegt daran, dass die Klassenmethode as_view eine neue Instanz und einen neuen Klassenversand erstellt und die "handler" -Funktion zurückgibt, die der Versand zurückgibt. Ich kann also in meiner klassenbasierten Sichtweise keine gemeinsame Methode verwenden, um es zu verspotten.

Ich kann die Modelle, die von der klassenbasierten Ansicht verwendet werden, ausspionieren, aber dann muss ich mein Ziel, "DRY" zu sein, grundlegend brechen und den Code sowohl in POST als auch in PUT kopieren. Ich denke, ich könnte den Code umgestalten und die Logik auf das Model übertragen. Aber ich bin nicht überzeugt, dass ich das machen will.

Wie können Sie eine gemeinsame Methode einer klassenbasierten Ansicht ausspionieren, um zu vermeiden, dass die Datenbank tatsächlich berührt wird? Vermeide sie einfach?

Antwort

3

Ich denke, Sie haben Ihre eigene Frage beantwortet. Die gleichen Dinge, die Sie zum Testen eines Web-Frameworks verwenden, gelten auch für Django, z. B. Inversion der Kontrolle und Abhängigkeitsinjektion. Sie können es in Python ziemlich einfach halten, also lassen Sie sich nicht einschüchtern oder abschalten von dem, was zum Beispiel in Spring existiert.

Warum verschieben Sie den Code nicht aus der klassenbasierten Sicht? Ihr Code wird immer noch nicht DRY sein, wenn Sie aus irgendeinem Grund die gleiche Logik an anderer Stelle benötigen. Nur weil es Django ist, bedeutet das nicht, dass gute Programmierprinzipien nicht zutreffen.

Ich würde vorschlagen, nur einige Dinge in neuen Klassen abstrahieren/Python-Module wie Dienste (als ein Konzept, nicht Django Definition von Diensten) und andere logische Abstraktionen für den Datenzugriff. Dann sind Sie unabhängig vom Request/Response-Lebenszyklus einer Django-View. Es gibt eine Tendenz von Django und Rails Entwicklern, jedes Bit der Logik direkt in das Modell oder die Ansicht zu setzen. Dies führt nur zu Gott-Klassen und Dingen, die schwer zu testen sind.

Sie können dies auch erleichtern, indem Sie Ihre Ansicht als eine leichte Abstraktion betrachten, die Dinge wie Marshalling Params (GET/POST) usw. mit dem Rest Ihres Codes behandelt und die an anderer Stelle eingekapselte Logik aufruft. IMO, wenn Sie testbaren Code möchten, 99% der Logik sollte außerhalb des Web-Kontext sein, es sei denn, es ist absolut entscheidend für den Prozess. Dies macht es auch einfacher, Dinge im Hintergrund und parallel zu betreiben.

Am Ende sollten normale Python-Module und Klassen stehen, die einfach zu testen sind, da sie keine direkten Abhängigkeiten zu HTTP haben. Wenn Sie HTTP nachahmen müssen, können Sie das Anforderungsobjekt einfach mockern. Sie haben Glück, dass die Kombination von Python/Django es leicht macht, diese Dinge als einfache dicts/kwargs auszugeben und zu verspotten.

Eine Sache, die ich mit klassenbasierten Sichten realisiert habe, ist die Verwendung mit Mixins und das Erzwingen einiger Konventionen (Rückgabe von JSON, offene Grapheigenschaften usw.), aber Verwendung der "fortgeschrittenen" klassenbasierten Sichten, die direkt Modelle benötigen wie DetailView erschweren die Dinge unnötig. Diese Ansichten sind gut für Admin-Bildschirme, aber für echte Apps helfen mehr als weh. Sie machen es schwer, Leistung zu testen und zu morden, wenn Sie keine nette, nahtlose Möglichkeit finden, Caching-Ebenen und ähnliches zu integrieren. An dieser Stelle ist es normalerweise einfach, von View oder TemplateView zu erben und damit fertig zu werden.

In Bezug auf DB Mocking speziell, erstellen Sie Ihre Mocks und gehen Sie durch Ihre Geschäftslogik. Dann spielt es keine Rolle, was Sie eingeben/ausgeben, solange es bestimmten Regeln und Schnittstellen entspricht. Sehen Sie etwas wie Mixer zum Beispiel. Sie können temporäre DBs auch während des Tests erstellen/löschen. Eine Möglichkeit besteht darin, separate Einstellungsmodule für dev/staging/production/testing usw. zu erstellen und diese abhängig von der Umgebung dynamisch zu laden. Auf diese Weise können Sie verhindern, dass Ihre Working Dev-Datenbank beim Ausführen von Komponententests beschädigt wird. Natürlich geht das mehr in eine Form des Integrationstests über, aber Sie sollten wahrscheinlich auch etwas davon machen. Die oben genannten Lösungen sind in anderen ORMs wie Hibernate üblich.

In Bezug auf die vorherige, können Sie etwas wie den folgenden Code in Ihren Einstellungen tun, um eine In-Memory-Datenbank für Komponententests zu verwenden. Schließlich obwohl, müssen Sie noch die Integrationstests gegen den tatsächlichen Datenspeichertyp, zum Beispiel MySQL

if 'test' in sys.argv: 
    DATABASES['default']['ENGINE'] = 'sqlite3' 

prüfen; TLDR

  1. Setzen Sie Ihre Logik außerhalb Klassenansichten in der richtigen Objekte und Module.

  2. Verhindern Sie nicht, dass die verschiedenen gebündelten klassenbasierten Ansichten für echte Apps und jeden Anwendungsfall funktionieren. Roll deinen eigenen.

  3. Verwenden Sie im Allgemeinen gute TDD-Prinzipien wie IOC, Übergabe der erforderlichen Parameter an Konstrukteure, lose Kopplung von Dingen, Vermeidung übermäßiger proprietärer Anforderungen (insbesondere HTTP).

    1. Vermeiden Sie die DB-Abhängigkeit, indem Sie Standard-Mock-Objekte erstellen (siehe # 3) und service-ähnliche Schnittstellen durchlaufen (siehe # 1).
Verwandte Themen