Zend_Test_PHPUnitZend_Test_PHPUnit bietet einen Testfall für MVC-Anwendungen, der Zusicherungen für Tests auf eine Vielzahl von Verantwortlichkeiten enthält. Um zu verstehen, was man damit machen kann, ist es wahrscheinlich am einfachsten, sich das folgende Beispiel anzusehen. Example #1 Beispiel eines Testfalls für ein Anwendungs-Login Das folgende ist ein einfacher Testfall für einen UserController, um verschiedene Dinge zu prüfen:
Dieses spezielle Beispiel setzt ein paar Dinge voraus. Zunächst verschieben wir das meiste unseres Bootstrappings in ein Plugin. Das vereinfacht das Setup des Testfalls, da es uns erlaubt, unsere Umgebung gezielt zu definieren und die Anwendung mit einer einzigen Zeile zu starten. Außerdem setzt unser spezielles Beispiel auch voraus, dass das automatische Laden von Klassen aktiviert ist, so dass wir uns nicht um das Laden der benötigten Klassen kümmern müssen (wie die richtigen Controller, Plugins, usw).
Dieses Beispiel könnte auch einfacher geschrieben werden -- nicht alle der gezeigten Zusicherungen sind notwendig. Hoffentlich zeigt es, wie einfach es sein kann, die eigene Anwendung zu testen. Bootstrapping der eigenen TestfälleWie im Login-Beispiel gezeigt, sollten alle MVC-Testfälle Zend_Test_PHPUnit_ControllerTestCase erweitern. Diese Klasse ihrerseits erweitert PHPUnit_Framework_TestCase und gibt einem alle Strukturen und Zusicherungen, die man von PHPUnit erwartet -- sowie einiges an Scaffolding und Zusicherungen, die genau auf die Zend Framework MVC-Implementation zugeschnitten sind. Um die eigene MVC-Anwendung zu testen, muß diese ein Bootstrap ausführen. Es gibt verschiedene Wege, dies zu tun, wobei sich alle der öffentlichen $bootstrap-Eigenschaft bedienen. Erstens und möglicherweise am zielgerichtetsten kann man einfach eine Instanz von Zend_Application erstellen, wie man es in der index.php machen würde und diese der $bootstrap-Eigenschaft zuweisen. Normalerweise macht man das in der setUp()-Methode; anschließend muss man parent::setUp() aufrufen:
Zweitens kann diese Eigenschaft so gesetzt werden, dass sie auf eine Datei zeigt. Wenn dieser Weg gewählt wird, sollte diese Datei nicht den Front-Controller ausführen, sondern stattdessen den Front-Controller konfigurieren und alles, was die Anwendung an speziellen Anforderungen benötigt.
Drittens kann ein PHP-Callback angegeben werden, der nach dem Bootstrap der Anwendung ausgeführt wird. Diese Methode kann im Login-Beispiel gesehen werden. Wenn das Callback eine Funktion oder statische Methode ist, könnte sie auch in der Klasse gesetzt werden:
In Fällen, in denen eine Objektinstanz notwendig ist, empfehlen wir die Durchführung in der eigenen setUp()-Methode:
Man beachte, dass parent::setUp() aufgerufen wird; das ist notwendig, da die setUp()-Methode von Zend_Test_PHPUnit_ControllerTestCase den Rest des Bootstrap-Prozesses durchführen wird (was den Aufruf des Callbacks einschließt). Während der normalen Anwendung wird die setUp()-Methode das Bootstrap der Anwendung ausführen. Dieser Prozess wird zunächst das Löschen der Umgebung enthalten, um einen sauberen Anfragestatus zu erhalten, das Zurücksetzen aller Plugins, Helfer und Antwortobjekte. Sobald das getan wurde, wird sie anschließend die Datei mit include() laden, die in $bootstrap angegeben ist oder den spezifizierten Callback aufrufen. Das Bootstrappen sollte so nahe wie möglich daran sein, wie die Anwendung das Bootstrap durchführt. Trotzdem gibt es einige Fallstricke:
Sobald die Anwendung das Bootstrapping ausgeführt hat, kann damit begonnen werden, eigene Tests zu erstellen. Testen eigener Controller und MVC AnwendungenSobald man sein Bootstrap hat, kann man mit dem Testen beginnen. Testen funktioniert grundsätzlich so, wie man es in einer PHPUnit-TestSuite erwarten würde, mit ein paar kleinen Unterschieden. Zuerst muss man eine URL ausführen, die getestet werden soll, indem die dispatch()-Methode des Testfalls ausgeführt wird:
Manchmal ist es trotzdem nötig, zusätzliche Informationen anzugeben -- GET und POST Variablen, COOKIE Informationen, usw. Man kann die Anfrage mit folgenden Informationen ausstatten:
Jetzt wurde die Anfrage durchgeführt, es ist also Zeit Zusicherungen zu prüfen. Controller Tests und der Redirector Action HelperImportant
Der Redirect Action Helper hat Probleme mit der Anweisung exit(), wenn die Methode gotoAndExit() verwendet wird und wird dann natürlich auch einen Test beenden, der für diese Methode läuft. Um die eigene Anwendung testbar zu machen, sollte diese Methode nicht am Redirector verwendet werden. Durch seine Natur führt das Redirector Action Helper Plugin ein Redirect aus und steigt nach diesem aus. Weil man Teile einer Anwendung nicht testen kann, wenn diese Exit-Aufrufe durchführen, deaktiviert Zend_Test_PHPUnit_ControllerTestCase den Exit-Teil des Redirectors automatisch, was zu unterschiedlichen Verhaltensweisen in Tests und echter Anwendung führen kann. Um sicherzustellen, dass der Redirect richtig arbeitet, sollte man diesen auf folgendem Weg durchführen:
Important
Abhängig von der eigenen Anwendung kann es sein, dass das nicht genug ist, da eine zusätzliche preDispatch()- oder postDispatch()-Logik ausgeführt werden könnte. Das kann aktuell mit Zend_Test auf keine vernünftige Weise behandelt werden. ZusicherungenZusicherungen sind das Herz der UnitTests; sie können verwendet werden um zu prüfen, ob die Ergebnisse das sind was man erwartet. Zu diesem Zweck bietet Zend_Test_PHPUnit_ControllerTestCase eine Anzahl an Zusicherungen, um das Testen eigener MVC-Anwendungen und Controller einfacher zu machen. CSS-Selektor-ZusicherungenCSS-Selektoren sind ein einfacher Weg um zu prüfen, dass bestimmte Teile im Inhalt der Antwort enthalten sind. Mit ihnen ist es auch trivial sicherzustellen, dass Elemente vorhanden sind, die für Javascript-UIs und/oder AJAX-Integrationen notwendig sind; die meisten JS-Toolkits bieten einige Mechanismen für das Abholen von DOM-Elementen an, die auf CSS-Selektoren basieren, so dass die Syntax die gleiche wäre. Diese Funktionalität wird über Zend_Dom_Query angeboten und in ein Set von 'Query'-Zusicherungen integriert. Jede dieser Zusicherungen nimmt als erstes Argument einen CSS-Selektor mit optional hinzugefügten Argumenten und/oder einer Fehlermeldung, basierend auf dem Typ der Zusicherung. Die Regeln für das Schreiben der CSS-Selektoren kann im Kapitel Theorie der Anwendung von Zend_Dom_Query gefunden werden. Abfragezusicherungen enthalten:
Zusätzlich hat jede der obigen Methoden eine 'Not'-Variante, die eine negative Zusicherung anbietet: assertNotQuery(), assertNotQueryContentContains(), assertNotQueryContentRegex() und assertNotQueryCount(). (Es ist zu beachten, dass die min und max Zählen keine dieser Varianten haben, was aus logischen Gründen so ist.) XPath-ZusicherungenEinige Entwickler sind mit XPath vertrauter als mit CSS-Selektoren, und deshalb werden für alle Abfrage Zusicherungen auch XPath-Varianten engeboten. Diese sind:
UmleitungszusicherungenOft wird eine Aktion umgeleitet. Statt der Umleitung zu folgen, erlaubt es Zend_Test_PHPUnit_ControllerTestCase, diese Umleitungen mit einer handvoll von Zusicherungen zu Testen.
Antwort-Header-ZusicherungenZusätzlich zur Prüfung auf Umleitungs-Header, ist es oft notwendig auf spezielle HTTP-Antwort-Codes und -Header zu prüfen -- zum Beispiel, um zu erkennen, ob eine Aktion eine 404 oder 500 Antwort hervorruft oder um sicherzustellen, dass JSON-Antworten die entsprechenden Content-Type-Header enthält. Die folgenden Zusicherungen sind vorhanden.
Zusätzlich hat jede der obigen Zusicherungen eine 'Not'-Variante für negative Zusicherungen. AnfragezusicherungenEs ist oft sinnvoll gegen die letzte Aktion, den Controller und das Modul zu prüfen; zusätzlich ist es möglich die genommene Route die prüfen. Die folgenden Zusicherungen können in diesen Fällen helfen:
Jede hat auch eine 'Not'-Variante für negative Zusicherungen. BeispieleZu wissen, wie man die eigene Infrastruktur für Tests einstellt und wie Zusicherungen zu erstellen sind, ist nur die halbe Miete; jetzt ist es Zeit, sich einige Testszenarien anzuschauen und herauszufinden, wie diese wirksam eingesetzt werden können. Example #2 Den UserController testen Betrachten wir eine Standardaufgabe für eine Webseite: Authentifizierung und Registrierung von Benutzern. In unserem Beispiel definieren wir einen UserController, um das zu behandeln und haben die folgenden Anforderungen:
Wir können und sollten zusätzliche Tests definieren, aber diese reichen vorerst aus. Für unsere Anwendung definieren wir ein Plugin, 'Initialisieren' es, damit es bei routeStartup() läuft. Das erlaubt es uns, das Bootstrapping in einem OOP-Interface zu kapseln, was auch einen einfachen Weg bietet, um ein Callback zu ermöglichen. Schauen wir uns erstmals die Grundlagen dieser Klasse an:
Das erlaubt es uns einen Bootstrap-Callback wie folgt zu erstellen:
Sobald das fertig ist, können wir unsere Tests schreiben. Was ist jedoch mit den Tests, die erfordern, dass der Benutzer angemeldet ist? Die einfache Lösung besteht darin, dass unsere Anwendungslogik das macht... und ein bisschen trickst, indem die Methoden resetRequest() und resetResponse() verwendet werden, die es uns erlauben eine andere Anfrage abzusetzen.
Jetzt schreiben wir Tests:
Es ist zu beachten, dass die Tests knapp sind und größtenteils nicht den aktuellen Inhalt suchen. Stattdessen suchen sie nach Teilen in der Anfrage -- Anfrage Codes und Header sowie DOM-Knoten. Das erlaubt es schnell zu prüfen, dass die Strukturen wie erwartet sind -- und verhindern, dass die Tests jedesmal scheitern, wenn der Site neue Inhalte hinzugefügt werden. Es ist auch zu beachten, dass wir die Struktur des Dokuments in unseren Tests verwenden. Zum Beispiel suchen wir im letzten Test nach einer Form, die einen Knoten der Klasse "errors" hat; das erlaubt es uns lediglich auf das Vorhandensein von Form-Prüfungsfehlern zu testen und uns keine Sorgen darüber zu machen, warum spezielle Fehler überhaupt geworfen werden. Diese Anwendung könnte eine Datenbank verwenden. Wenn dem so ist, muss man wahrscheinlich einige Grundlagen ändern um sicherzustellen, dass die Datenbank am Anfang jedes Tests in einer unverfälschten, testbaren Konfiguration ist. PHPUnit bietet bereits Funktionalität um das sicherzustellen; » Lesen Sie darüber in der PHPUnit-Dokumentation nach. Wir empfehlen eine separate Datenbank für das Testen zu verwenden statt der Produktionsdatenbank und entweder eine SQLite-Datei oder eine Datenbank im Speicher zu verwenden, da beide Optionen sehr performant sind, keinen separaten Server benötigen und die meisten SQL-Syntax verwenden können.
|