| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- <?xml version="1.0" encoding="UTF-8"?>
- <!-- EN-Revision: 24249 -->
- <!-- Reviewed: 22236 -->
- <sect2 id="zend.test.phpunit.examples">
- <title>Beispiele</title>
- <para>
- Zu 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.
- </para>
- <example id="zend.test.phpunit.examples.userController">
- <title>Den UserController testen</title>
- <para>
- 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:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- Wenn ein Benutzer nicht authentifiziert ist, wird er immer zur Login-Seite des
- Controllers umgeleitet, unabhängig von der angeforderten Aktion.
- </para>
- </listitem>
- <listitem>
- <para>
- Die Login-Formularseite wird sowohl das Login-Formular als auch das
- Registrationsformular anzeigen.
- </para>
- </listitem>
- <listitem>
- <para>
- Die Angabe von ungültigen Anmeldedaten soll zur Anzeige des Login-Formulars
- führen.
- </para>
- </listitem>
- <listitem>
- <para>
- Das Ansehen der Anmeldedaten soll zu einer Umleitung zur Profilseite des
- Benutzers führen.
- </para>
- </listitem>
- <listitem>
- <para>
- Die Profilseite soll angepasst werden, um den Benutzernamen des Benutzers
- anzuzeigen.
- </para>
- </listitem>
- <listitem>
- <para>
- Authentifizierte Benutzer, welche die Loginseite besuchen, sollen zu ihrer
- Profilseite umgeleitet werden.
- </para>
- </listitem>
- <listitem>
- <para>
- Bei der Abmeldung soll ein Benutzer zur Loginseite umgeleitet werden.
- </para>
- </listitem>
- <listitem>
- <para>
- Mit ungültigen Daten soll die Registrierung fehlschlagen.
- </para>
- </listitem>
- </itemizedlist>
- <para>
- Wir können und sollten zusätzliche Tests definieren, aber diese reichen vorerst aus.
- </para>
- <para>
- Für unsere Anwendung definieren wir ein Plugin, 'Initialisieren' es, damit es bei
- <methodname>routeStartup()</methodname> 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:
- </para>
- <programlisting language="php"><![CDATA[
- class Bugapp_Plugin_Initialize extends Zend_Controller_Plugin_Abstract
- {
- /**
- * @var Zend_Config
- */
- protected static $_config;
- /**
- * @var string Aktuelle Umgebung
- */
- protected $_env;
- /**
- * @var Zend_Controller_Front
- */
- protected $_front;
- /**
- * @var string Pfad zum Root der Anwendung
- */
- protected $_root;
- /**
- * Constructor
- *
- * Umgebung, Root Pfad und Konfiguration initialisieren
- *
- * @param string $env
- * @param string|null $root
- * @return void
- */
- public function __construct($env, $root = null)
- {
- $this->_setEnv($env);
- if (null === $root) {
- $root = realpath(dirname(__FILE__) . '/../../../');
- }
- $this->_root = $root;
- $this->initPhpConfig();
- $this->_front = Zend_Controller_Front::getInstance();
- }
- /**
- * Route beginnen
- *
- * @return void
- */
- public function routeStartup(Zend_Controller_Request_Abstract $request)
- {
- $this->initDb();
- $this->initHelpers();
- $this->initView();
- $this->initPlugins();
- $this->initRoutes();
- $this->initControllers();
- }
- // Die Definition von Methoden würde hier folgen...
- }
- ]]></programlisting>
- <para>
- Das erlaubt es uns einen Bootstrap-Callback wie folgt zu erstellen:
- </para>
- <programlisting language="php"><![CDATA[
- class UserControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
- {
- public function appBootstrap()
- {
- $controller = $this->getFrontController();
- $controller->registerPlugin(
- new Bugapp_Plugin_Initialize('development')
- );
- }
- public function setUp()
- {
- $this->bootstrap = array($this, 'appBootstrap');
- parent::setUp();
- }
- // ...
- }
- ]]></programlisting>
- <para>
- 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
- <methodname>resetRequest()</methodname> und <methodname>resetResponse()</methodname>
- verwendet werden, die es uns erlauben eine andere Anfrage abzusetzen.
- </para>
- <programlisting language="php"><![CDATA[
- class UserControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
- {
- // ...
- public function loginUser($user, $password)
- {
- $this->request->setMethod('POST')
- ->setPost(array(
- 'username' => $user,
- 'password' => $password,
- ));
- $this->dispatch('/user/login');
- $this->assertRedirectTo('/user/view');
- $this->resetRequest()
- ->resetResponse();
- $this->request->setPost(array());
- // ...
- }
- // ...
- }
- ]]></programlisting>
- <para>
- Jetzt schreiben wir Tests:
- </para>
- <programlisting language="php"><![CDATA[
- class UserControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
- {
- // ...
- public function testCallWithoutActionShouldPullFromIndexAction()
- {
- $this->dispatch('/user');
- $this->assertController('user');
- $this->assertAction('index');
- }
- public function testLoginFormShouldContainLoginAndRegistrationForms()
- {
- $this->dispatch('/user');
- $this->assertQueryCount('form', 2);
- }
- public function testInvalidCredentialsShouldResultInRedisplayOfLoginForm()
- {
- $request = $this->getRequest();
- $request->setMethod('POST')
- ->setPost(array(
- 'username' => 'bogus',
- 'password' => 'reallyReallyBogus',
- ));
- $this->dispatch('/user/login');
- $this->assertNotRedirect();
- $this->assertQuery('form');
- }
- public function testValidLoginShouldRedirectToProfilePage()
- {
- $this->loginUser('foobar', 'foobar');
- }
- public function testAuthenticatedUserShouldHaveCustomizedProfilePage()
- {
- $this->loginUser('foobar', 'foobar');
- $this->request->setMethod('GET');
- $this->dispatch('/user/view');
- $this->assertNotRedirect();
- $this->assertQueryContentContains('h2', 'foobar');
- }
- public function
- testAuthenticatedUsersShouldBeRedirectedToProfileWhenVisitingLogin()
- {
- $this->loginUser('foobar', 'foobar');
- $this->request->setMethod('GET');
- $this->dispatch('/user');
- $this->assertRedirectTo('/user/view');
- }
- public function testUserShouldRedirectToLoginPageOnLogout()
- {
- $this->loginUser('foobar', 'foobar');
- $this->request->setMethod('GET');
- $this->dispatch('/user/logout');
- $this->assertRedirectTo('/user');
- }
- public function testRegistrationShouldFailWithInvalidData()
- {
- $data = array(
- 'username' => 'This will not work',
- 'email' => 'this is an invalid email',
- 'password' => 'Th1s!s!nv@l1d',
- 'passwordVerification' => 'wrong!',
- );
- $request = $this->getRequest();
- $request->setMethod('POST')
- ->setPost($data);
- $this->dispatch('/user/register');
- $this->assertNotRedirect();
- $this->assertQuery('form .errors');
- }
- }
- ]]></programlisting>
- <para>
- 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.
- </para>
- <para>
- 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.
- </para>
- <para>
- Diese Anwendung <emphasis>könnte</emphasis> 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; <ulink
- url="http://www.phpunit.de/manual/3.4/en/database.html">Lesen Sie darüber in
- der PHPUnit-Dokumentation nach</ulink>. 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 <acronym>SQL</acronym>-Syntax verwenden
- können.
- </para>
- </example>
- </sect2>
|