Mit Einheitentests können Sie die Qualität Ihres Codes überprüfen, nachdem Sie ihn geschrieben haben. Sie können mit diesen Tests aber auch Ihren Entwicklungsprozess verbessern. Anstatt die Tests nach der Entwicklung der Anwendung zu schreiben, sollten Sie sie besser währenddessen schreiben. So können Sie kleine, wartbare, wiederverwendbare Codeeinheiten entwickeln. Außerdem können Sie Ihren Code dadurch sorgfältiger und schneller testen.
Lokale Einheitentests führen Sie in Ihrer eigenen Entwicklungsumgebung und ohne Einbeziehung von Remote-Komponenten aus. App Engine stellt Testdienstprogramme bereit, die lokale Implementierungen von Datastore und anderen App Engine-Diensten verwenden. Mithilfe von Dienst-Stubs können Sie lokal testen, wie Ihr Code diese Dienste nutzt, ohne den Code in App Engine bereitzustellen.
Ein Dienst-Stub ist eine Methode, die das Verhalten des Diensts simuliert. Mit dem Datenspeicherdienst-Stub, der unter Datenspeicher- und Memcache-Tests schreiben dargestellt wird, können Sie beispielsweise Ihren Datenspeichercode testen, ohne dass Anfragen an den realen Datenspeicher gesendet werden. Eine Entität, die während eines Datenspeicher-Einheitentests gespeichert wird, verbleibt im Arbeitsspeicher, nicht im Datenspeicher, und wird im Anschluss an den Test gelöscht. Sie können kurze, schnelle Tests unabhängig vom Datenspeicher ausführen.
In diesem Dokument wird erläutert, wie Sie Einheitentests für verschiedene lokale App Engine-Dienste schreiben. Außerdem erfahren Sie, wie Sie ein Test-Framework einrichten.
Einführung in die Python 2-Testdienstprogramme
Ein App Engine-Python-Modul namens testbed
stellt Dienst-Stubs zum Testen von Komponenten zur Verfügung.
Dienst-Stubs stehen für folgende Dienste zur Verfügung:
- Anwendungsidentität
init_app_identity_stub
- Blobstore (
init_blobstore_stub
verwenden) - Funktion (
init_capability_stub
verwenden) - Datastore (
init_datastore_v3_stub
verwenden) - Dateien (
init_files_stub
verwenden) - Images (nur für dev_appserver;
init_images_stub
verwenden) - LogService (
init_logservice_stub
verwenden) - Mail (
init_mail_stub
verwenden) - Memcache (
init_memcache_stub
verwenden) - Aufgabenwarteschlange (
init_taskqueue_stub
verwenden) - URL Fetch (
init_urlfetch_stub
verwenden) - Nutzerdienst (
init_user_stub
verwenden)
Initialisieren Sie alle Stubs gleichzeitig mit init_all_stubs
.
Datenspeicher- und Memcache-Tests schreiben
In diesem Abschnitt wird anhand eines Beispiels erläutert, wie Sie Code schreiben, mit dem Sie die Verwendung von datastore- und memcache-Diensten testen.
Achten Sie darauf, dass Ihr Test-Runner die entsprechenden Bibliotheken im Ladepfad von Python hat, einschließlich der App Engine-Bibliotheken, yaml
im App Engine-SDK, des Anwendungsstamms und anderer vom Anwendungscode erwarteter Änderungen am Bibliothekspfad (beispielsweise ein lokales ./lib
-Verzeichnis, falls vorhanden). Beispiel:
import sys
sys.path.insert(1, 'google-cloud-sdk/platform/google_appengine')
sys.path.insert(1, 'google-cloud-sdk/platform/google_appengine/lib/yaml/lib')
sys.path.insert(1, 'myapp/lib')
Importieren Sie das Python-unittest
-Modul und die App Engine-Module, die für die getesteten Dienste relevant sind, in diesem Fall memcache
und ndb
, die sowohl den Datenspeicher als auch Memcache verwenden. Importieren Sie auch das testbed
-Modul.
Erstellen Sie anschließend eine TestModel
-Klasse. In diesem Beispiel wird von einer Funktion überprüft, ob in Memcache eine Entität gespeichert ist. Wenn keine Entität gefunden wird, prüft die Funktion den Datenspeicher hinsichtlich einer vorhandenen Entität. In der Praxis ist dies häufig überflüssig, da ndb
Memcache im Hintergrund verwendet. Für einen Test ist es jedoch ein geeignetes Muster.
Erstellen Sie als Nächstes einen Testfall. Unabhängig von den zu testenden Diensten muss der Testfall eine Testbed
-Instanz erstellen und aktivieren. Der Testfall muss außerdem die relevanten Dienst-Stubs initialisieren, in diesem Fall mit init_datastore_v3_stub
und init_memcache_stub
. Die Methoden zum Initialisieren anderer App Engine-Dienst-Stubs finden Sie unter Einführung in die Python-Testdienstprogramme.
Bei der init_datastore_v3_stub()
-Methode ohne Argument wird ein arbeitsspeicherinterner Datenspeicher verwendet, der anfangs leer ist. Wenn Sie eine bestehende Datenspeicher-Entität testen möchten, müssen Sie ihren Pfadnamen als Argument für init_datastore_v3_stub()
aufnehmen.
Nehmen Sie zusätzlich zu setUp()
auch die Methode tearDown()
auf, die die Prüfumgebung deaktiviert. Dadurch werden die ursprünglichen Stubs wiederhergestellt, sodass die Tests sich nicht gegenseitig behindern.
Implementieren Sie anschließend die Tests.
Nun können Sie mit TestModel
Tests schreiben, die anstelle der eigentlichen Dienste die Dienst-Stubs für den Datenspeicher oder für Memcache verwenden.
Bei der unten angezeigten Methode werden beispielsweise zwei Entitäten erstellt: Die erste Entität verwendet den Standardwert für das number
-Attribut (42), das zweite verwendet einen Nicht-Standardwert für number
(17). Anschließend erstellt die Methode eine Anfrage für TestModel
-Entitäten, jedoch nur für diejenigen mit dem Standardwert für number
.
Nach dem Abrufen aller übereinstimmenden Entitäten testet die Methode, ob genau eine Entität gefunden wurde und ob es sich bei dem Wert des Attributs number
dieser Entität um den Standardwert handelt.
Hier ein weiteres Beispiel: Die folgende Methode erstellt eine Entität und ruft sie mit der oben erstellten GetEntityViaMemcache()
-Funktion ab. Die Methode testet dann, ob eine Entität zurückgegeben wurde und der Wert number
derselbe ist wie für die zuvor erstellte Entität.
Rufen Sie abschließend unittest.main()
auf.
Wie Sie Tests ausführen, erfahren Sie unter Tests ausführen.
Cloud Datastore-Tests schreiben
Wenn Ihre Anwendung Cloud Datastore verwendet, können Sie Tests schreiben, mit denen das Verhalten Ihrer Anwendung im Hinblick auf Eventual Consistency überprüft wird.
Von db.testbed
werden Optionen bereitgestellt, die dies vereinfachen:
Mit der Klasse PseudoRandomHRConsistencyPolicy
können Sie die Wahrscheinlichkeit steuern, mit der vor jeder globalen Anfrage (ohne Ancestors) ein Schreibvorgang übernommen wird. Wenn Sie die Wahrscheinlichkeit auf 0 % setzen, weisen Sie den Datenspeicher-Stub an, maximale Eventual Consistency anzuwenden. Maximale Eventual Consistency bedeutet, dass für Schreibvorgänge ein Commit durchgeführt wird, aber bei ihrer Anwendung immer ein Fehler auftritt. Aus der Sicht globaler (Nicht-Ancestor-)Anfragen treten somit konsistent keinerlei Änderungen auf. Dies ist nicht für das Maß an Eventual Consistency repräsentativ, das während der Ausführung der Anwendung in der Produktion auftritt. Für Testzwecke ist es jedoch sehr hilfreich, den lokalen Datenspeicher so konfigurieren zu können, dass er sich jedes Mal gleich verhält. Wenn Sie eine Wahrscheinlichkeit ungleich 0 verwenden, nimmt PseudoRandomHRConsistencyPolicy
eine deterministische Abfolge von Konsistenzentscheidungen vor, sodass die Testergebnisse konsistent sind:
Mithilfe der Test-APIs können Sie überprüfen, ob sich Ihre Anwendung in Bezug auf Eventual Consistency ordnungsgemäß verhält. Beachten Sie jedoch, dass das lokale High Replication-Lesekonsistenzmodell eine Annäherung an das in der Produktion verwendete High Replication-Lesekonsistenzmodell und kein genaues Replikat ist. In der lokalen Umgebung führt die Ausführung von get()
für Entity
, die zu einer Entitätengruppe mit einem nicht angewendeten Schreibvorgang gehört, immer dazu, dass die Ergebnisse des nicht angewendeten Schreibvorgangs für nachfolgende globale Abfragen sichtbar werden. In der Produktion ist dies nicht der Fall.
Mail-Tests schreiben
Mit dem E-Mail-Dienst-Stub können Sie den E-Mail-Dienst testen. Ähnlich wie bei anderen von testbed unterstützten Diensten initialisieren Sie zuerst den Stub. Dann rufen Sie den Code auf, der die Mail API verwendet, und testen schließlich, ob die richtigen Nachrichten gesendet wurden.
Aufgabenwarteschlangen-Tests schreiben
Mit dem taskqueue-Stub können Sie Tests schreiben, die den taskqueue verwenden. Ähnlich wie bei anderen von testbed unterstützten Diensten initialisieren Sie zuerst den Stub. Dann rufen Sie den Code auf, der die Taskqueue API verwendet, und testen schließlich, ob die Aufgaben der Warteschlange ordnungsgemäß hinzugefügt wurden.
Konfigurationsdatei queue.yaml
festlegen
Wenn Sie Tests für Code ausführen möchten, der mit einer nicht standardmäßigen Warteschlange interagiert, müssen Sie eine queue.yaml
-Datei für Ihre Anwendung erstellen und angeben.
Ein Beispiel für queue.yaml
:
Weitere Informationen zu den für queue.yaml verfügbaren Optionen finden Sie unter Konfiguration der Aufgabenwarteschlange.
Die Position von queue.yaml
wird beim Initialisieren des Stubs angegeben:
self.testbed.init_taskqueue_stub(root_path='.')
Im Beispiel befindet sich queue.yaml
in demselben Verzeichnis wie die Tests. Wenn es in einem anderen Ordner wäre, müsste dieser Pfad in root_path
angegeben werden.
Aufgaben filtern
Mit den taskqueue-Stub-get_filtered_tasks
können Sie Aufgaben in der Warteschlange filtern.
Dies erleichtert das Schreiben von Tests zum Überprüfen von Code, mit dem mehrere Aufgaben in die Warteschlange gestellt werden.
Tests für zurückgestellte Aufgaben schreiben
Wenn Ihr Anwendungscode die zurückgestellte Bibliothek verwendet, können Sie den taskqueue-Stub zusammen mit deferred
verwenden, um zu kontrollieren, ob zurückgestellte Funktionen in die Warteschlange gestellt und korrekt ausgeführt werden.
Standardumgebungsvariablen ändern
App Engine-Dienste sind oft von Umgebungsvariablen abhängig. Die Methode activate()
der Klasse testbed.Testbed
verwendet dafür Standardwerte. Sie können jedoch mit den Werten benutzerdefinierte Werte entsprechend Ihren Testanforderungen mit der Methode setup_env
der Klasse testbed.Testbed
festlegen.
Nehmen wir beispielsweise an, Sie verwenden einen Test, bei dem mehrere Entitäten im Datenspeicher gespeichert werden, die alle mit derselben Anwendungs-ID verknüpft sind. Nun möchten Sie dieselben Tests noch einmal ausführen, diesmal jedoch mit einer anderen Anwendungs-ID als der, die mit den gespeicherten Entitäten verknüpft ist. Übergeben Sie dazu den neuen Wert als self.setup_env()
an app_id
.
Beispiel:
Anmeldung simulieren
Mit setup_env
wird oft auch ein angemeldeter Nutzer mit oder ohne Administratorrechten simuliert. Sie prüfen damit, ob Ihre Handler in jedem Fall ordnungsgemäß funktionieren.
Nun können Ihre Testmethoden beispielsweise self.loginUser('', '')
aufrufen, um zu simulieren, dass kein Nutzer angemeldet ist. Mit self.loginUser('test@example.com', '123')
simulieren Sie, dass ein Nutzer ohne Administratorrechte angemeldet ist, und mit self.loginUser('test@example.com',
'123', is_admin=True)
, dass ein Administrator angemeldet ist.
Test-Framework einrichten
Die Testdienstprogramme des SDK sind nicht an ein bestimmtes Framework gebunden. Sie können Ihre Einheitentests mit jedem verfügbaren App Engine-Test-Runner wie etwa nose-gae oder ferrisnose ausführen. Sie können auch selbst einen einfachen Test-Runner schreiben oder den unten angezeigten Test-Runner verwenden.
Für die folgenden Skripts wird das unittest von Python verwendet.
Sie können dem Skript jeden beliebigen Namen geben. Geben Sie bei der Ausführung den Pfad zu Ihrer Google Cloud CLI- oder Google App Engine SDK-Installation sowie den Pfad zu Ihren Testmodulen an. Das Skript erkennt alle Tests im angegebenen Pfad und gibt die Ergebnisse an den Standardfehlerstream aus. Testdateien folgen der Konvention, dass test
ihrem Namen vorangestellt ist.
Tests ausführen
Sie können diese Tests einfach mit dem Skript runner.py
ausführen, das ausführlich unter Test-Framework einrichten beschrieben wird:
python runner.py <path-to-appengine-or-gcloud-SDK> .