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 ohne Abhängigkeit vom Datenspeicher selbst ausführen.
Dieses Dokument enthält einige Informationen zum Einrichten eines Test-Frameworks und beschreibt dann, wie Einheitentests für mehrere lokale App Engine-Dienste geschrieben werden.
Test-Framework einrichten
Auch wenn die Testfunktionen des SDK nicht an ein bestimmtes Framework gebunden sind, wird für die Beispiele in diesem Leitfaden JUnit verwendet, sodass Sie ein konkretes und komplettes Framework zum Arbeiten haben. Bevor Sie mit dem Schreiben von Tests beginnen, müssen Sie das entsprechende JUnit 4 JAR zu Ihrem Testklassenpfad hinzufügen. Danach können Sie einen sehr einfachen JUnit-Test schreiben.
Wenn Sie Eclipse verwenden, müssen Sie die Quelldatei des auszuführenden Tests auswählen. Wählen Sie das Menü Ausführen > Ausführen als > JUnit-Test aus. Die Ergebnisse des Tests werden im Konsolenfenster angezeigt.
Einführung in die Java 8-Testfunktionen
MyFirstTest
zeigt die einfachste mögliche Testeinrichtung. Für Tests, die nicht von App Engine-APIs oder lokalen Dienstimplementierungen abhängig sind, benötigen Sie möglicherweise nichts anderes mehr. Wenn jedoch Ihre Tests oder der getestete Code diese Abhängigkeiten haben, fügen Sie Ihrem Testklassenpfad die folgenden JAR-Dateien hinzu:
${SDK_ROOT}/lib/impl/appengine-api.jar
${SDK_ROOT}/lib/impl/appengine-api-stubs.jar
${SDK_ROOT}/lib/appengine-tools-api.jar
Über diese JAR-Dateien stehen die Laufzeit-APIs und die lokalen Implementierungen dieser APIs für Ihre Tests zur Verfügung.
App Engine-Dienste erwarten eine Reihe von Voraussetzungen von ihrer Ausführungsumgebung und das Einrichten dieser Voraussetzungen erfordert recht viel Standardcode. Sie müssen diese Voraussetzungen nicht selbst einrichten, sondern können die Funktionen im com.google.appengine.tools.development.testing
-Paket verwenden. Fügen Sie Ihrem Testklassenpfad die folgende JAR-Datei hinzu, um dieses Paket zu verwenden:
${SDK_ROOT}/lib/testing/appengine-testing.jar
Suchen Sie in Javadoc nach dem Paket com.google.appengine.tools.development.testing
. Die wichtigste Klasse in diesem Paket ist LocalServiceTestHelper, die alle erforderlichen Umgebungseinstellungen verarbeitet und Ihnen einen Konfigurationspunkt auf oberster Ebene für alle lokalen Dienste bietet, auf die Sie in Ihren Tests möglicherweise zugreifen möchten.
So schreiben Sie einen Test, der auf einen bestimmten lokalen Dienst zugreift:
- Erstellen Sie eine Instanz von
LocalServiceTestHelper
mit einerLocalServiceTestConfig
-Implementierung für diesen speziellen lokalen Dienst. - Rufen Sie vor jedem Test
setUp()
und nach jedem TesttearDown()
für IhreLocalServiceTestHelper
-Instanz auf.
Datenspeicher- und Memcache-Tests schreiben
Im folgenden Beispiel wird die Verwendung des Datenspeicherdiensts getestet.
In diesem Beispiel sorgt LocalServiceTestHelper
für das Einrichten und Entfernen der Teile der Ausführungsumgebung, die allen lokalen Diensten gemeinsam sind, und LocalDatastoreServiceTestConfig
sorgt für das Einrichten und Entfernen der Teile der Ausführungsumgebung, die für den lokalen Datenspeicherdienst spezifisch sind. In Javadoc wird beschrieben, dass der lokale Datenspeicherdienst so konfiguriert wird, dass alle Daten im Arbeitsspeicher verbleiben (und nicht in regelmäßigen Abständen auf das Laufwerk geleert werden), und alle In-Memory-Daten am Ende jedes Tests gelöscht werden. Hierbei handelt es sich um das Standardverhalten für einen Datenspeichertest. Ändern Sie dieses Verhalten, wenn es nicht Ihren Vorstellungen entspricht.
Änderung des Beispiels zum Zugreifen auf den Memcache anstelle des Datenspeichers
Zum Erstellen eines Tests, bei dem auf den lokalen Memcache-Dienst zugegriffen wird, verwenden Sie den unten dargestellten Code mit einigen kleinen Änderungen.
Anstatt mit dem Datenspeicher verbundene Klassen zu importieren, importieren Sie Klassen, die mit dem Memcache verbunden sind. Es ist dennoch erforderlich, dass Sie LocalServiceTestHelper
importieren.
Ändern Sie den Namen der Klasse, die Sie erstellen, und ändern Sie die LocalServiceTestHelper
-Instanz von so, dass sie für den Memcache spezifisch sind.
Und ändern Sie schließlich die Art, in der Sie den Test durchführen, damit er für den Memcache relevant ist.
Wie im Beispiel des Datenspeichers wird die Ausführungsumgebung von LocalServiceTestHelper
und der dienstspezifischen LocalServiceTestConfig
(in diesem Fall LocalMemcacheServiceTestConfig
) verwaltet.
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 LocalDatastoreServiceTestConfig
werden Optionen bereitgestellt, die dies vereinfachen:
Durch Festlegen des Prozentsatzes nicht angewendeter Jobs auf 100 wird der lokale Datenspeicher angewiesen, die maximale Menge der Eventual Consistency zu nutzen. Maximale Eventual Consistency bedeutet, dass für Schreibvorgänge ein Commit ausgeführt wird, aber bei ihrer Anwendung ein Fehler auftritt. Aus diesem Grund erfolgen für globale Suchanfragen (ohne übergeordnetes Element) niemals Änderungen. Dies ist natürlich nicht repräsentativ für die Menge der Eventual Consistency, die bei Ihrer Anwendung während der Ausführung in der Produktion auftritt. Aber zu Testzwecken ist es sehr hilfreich, den lokalen Datenspeicher so konfigurieren zu können, dass er sich jedes Mal so verhält.
Wenn Sie eine genauere Kontrolle darüber haben möchten, welche Transaktionen nicht zutreffen, können Sie Ihre eigene HighRepJobPolicy
registrieren:
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.
Aufgabenwarteschlangen-Tests schreiben
Tests, die die lokale Aufgabenwarteschlange verwenden, sind etwas komplizierter, da hier im Gegensatz zu Datenspeicher und Memcache die Task Queue API keine Möglichkeit zur Untersuchung des Dienststatus bietet. Wir müssen auf die lokale Aufgabenwarteschlange selbst zugreifen, um zu prüfen, ob eine Aufgabe mit den erwarteten Parametern geplant wurde. Dazu wird com.google.appengine.api.taskqueue.dev.LocalTaskQueue
benötigt.
Beachten Sie, wie wir von LocalTaskqueueTestConfig
ein Handle der lokalen Dienstinstanz anfordern und anschließend selbst den lokalen Dienst auf die ordnungsgemäße Planung der Aufgabe hin überprüfen. Alle LocalServiceTestConfig
-Implementierungen weisen eine ähnliche Methode auf. Sie werden sie nicht immer brauchen, aber früher oder später werden Sie froh sein, dass es sie gibt.
Konfigurationsdatei queue.xml
festlegen
Die Aufgabenwarteschlangen-Testbibliotheken ermöglichen auf einer Pro-LocalServiceTestHelper-Basis die Angabe einer beliebigen Anzahl von queue.xml
-Konfigurationen mithilfe der Methode LocalTaskQueueTestConfig.setQueueXmlPath
. Derzeit werden die Einstellungen für die Ratenbegrenzung einer Warteschlange vom lokalen Entwicklungsserver ignoriert.
Es ist nicht möglich, simultane Aufgaben gleichzeitig lokal auszuführen.
Beispiel: Ein Projekt muss möglicherweise mit der Datei queue.xml
getestet werden, die von der App Engine-Anwendung hochgeladen und verwendet wird. Angenommen, die Datei queue.xml
befindet sich am Standardspeicherort. In diesem Fall könnte der obige Beispielcode wie folgt geändert werden, um Testzugriff auf die Warteschlangen in der Datei src/main/webapp/WEB-INF/queue.xml
zu gewähren:
Passen Sie den Pfad zur queue.xml
-Datei an die Dateistruktur Ihres Projekts an.
Verwenden Sie die Methode QueueFactory.getQueue
, um auf Warteschlangen nach Namen zuzugreifen:
Tests für zurückgestellte Aufgaben schreiben
Wenn Ihr Anwendungscode zurückgestellte Aufgaben verwendet, erleichtern die Java-Testfunktionen das Schreiben eines Integrationstests, der die Ergebnisse dieser Aufgaben überprüft.
Wie bei unserem ersten Beispiel einer Warteschlange für lokale Aufgaben verwenden wir LocalTaskqueueTestConfig
. Diesmal initialisieren wir sie jedoch mit einigen zusätzlichen Argumenten, die uns ermöglichen, nicht nur zu überprüfen, dass die Aufgabe geplant war, sondern dass die Aufgabe ausgeführt wurde: Wir rufen setDisableAutoTaskExecution(false)
auf, damit die Warteschlange für lokale Aufgaben automatisch Aufgaben ausführt. Wir rufen setCallbackClass(LocalTaskQueueTestConfig.DeferredTaskCallback.class)
auf, um der Aufgabenwarteschlange mitzuteilen, dass ein Callback verwendet werden soll, das nachvollzieht, wie zurückgestellte Aufgaben ausgeführt werden. Und schließlich rufen wir setTaskExecutionLatch(latch)
auf, um der Aufgabenwarteschlange mitzuteilen, dass das Auffangregister nach jeder Aufgabenausführung verringert werden soll. Mit dieser Konfiguration können wir einen Test schreiben, bei dem wir eine zurückgestellte Aufgabe in die Warteschlange stellen, warten, bis diese Aufgabe ausgeführt wird, und dann überprüfen, ob sich die Aufgabe während der Ausführung wie erwartet verhalten hat.
Funktionstests für lokale Dienste schreiben
Funktionstests beinhalten das Ändern des Status eines Diensts, z. B. Datenspeicher, Blobstore, Memcache, und das Ausführen der Anwendung für diesen Dienst. So können Sie prüfen, ob Ihre Anwendung unter verschiedenen Bedingungen erwartungsgemäß reagiert. Der Funktionsstatus kann mithilfe der Klasse LocalCapabilitiesServiceTestConfig geändert werden.
Das folgende Code-Snippet ändert den Funktionsstatus des Datenspeicherdiensts in "deaktiviert" und führt anschließend einen Test für den Datenspeicherdienst aus. Sie können den Datenspeicher nach Bedarf durch andere Dienste ersetzen.
Der Beispieltest erstellt zuerst ein Capability
-Objekt, das für den Datenspeicher initialisiert wird, und erstellt dann ein CapabilityStatus
-Objekt, das auf DISABLED gesetzt ist. Die LocalCapabilitiesServiceTestConfig
wird erstellt, wobei die Funktion und der Status mithilfe der gerade erstellten Objekte Capability
und CapabilityStatus
festgelegt werden.
LocalServiceHelper
wird dann mit dem Objekt LocalCapabilitiesServiceTestConfig
erstellt. Nun, da der Test eingerichtet wurde, wird DatastoreService
erstellt und eine Abfrage gesendet, um festzustellen, ob der Test die erwarteten Ergebnisse generiert, in diesem Fall ein CapabilityDisabledException
.
Tests für andere Dienste schreiben
Die Testfunktionen sind für Blobstore und andere App Engine-Dienste verfügbar. Eine Liste aller Dienste mit lokalen Implementierungen für Tests finden Sie in der Dokumentation zu LocalServiceTestConfig
.
Tests mit Authentifizierungsvorgaben schreiben
In diesem Beispiel wird gezeigt, wie Sie Tests zum Überprüfen der Logik schreiben, die UserService verwendet, um festzustellen, ob ein Nutzer angemeldet ist oder über Administratorberechtigungen verfügt. Beachten Sie, dass jeder Nutzer mit der einfachen Rolle "Betrachter", "Bearbeiter" oder "Inhaber" oder der vordefinierten Rolle "App Engine-Anwendungsadministrator" über Administratorberechtigungen verfügt.
In diesem Beispiel konfigurieren wir den LocalServiceTestHelper
mit LocalUserServiceTestConfig
, damit wir den UserService
in unserem Test verwenden können. Wir konfigurieren jedoch auch einige authentifizierungsbezogene Umgebungsdaten auf dem LocalServiceTestHelper
selbst.
In diesem Beispiel konfigurieren wir den LocalServiceTestHelper
mit dem LocalUserServiceTestConfig
damit wir OAuthService
verwenden können.