Zend_Test-PHPUnit-Examples.xml 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- EN-Revision: 24249 -->
  3. <!-- Reviewed: 22236 -->
  4. <sect2 id="zend.test.phpunit.examples">
  5. <title>Beispiele</title>
  6. <para>
  7. Zu wissen, wie man die eigene Infrastruktur für Tests einstellt und wie Zusicherungen zu
  8. erstellen sind, ist nur die halbe Miete; jetzt ist es Zeit, sich einige Testszenarien
  9. anzuschauen und herauszufinden, wie diese wirksam eingesetzt werden können.
  10. </para>
  11. <example id="zend.test.phpunit.examples.userController">
  12. <title>Den UserController testen</title>
  13. <para>
  14. Betrachten wir eine Standardaufgabe für eine Webseite: Authentifizierung und Registrierung
  15. von Benutzern. In unserem Beispiel definieren wir einen UserController, um das zu
  16. behandeln und haben die folgenden Anforderungen:
  17. </para>
  18. <itemizedlist>
  19. <listitem>
  20. <para>
  21. Wenn ein Benutzer nicht authentifiziert ist, wird er immer zur Login-Seite des
  22. Controllers umgeleitet, unabhängig von der angeforderten Aktion.
  23. </para>
  24. </listitem>
  25. <listitem>
  26. <para>
  27. Die Login-Formularseite wird sowohl das Login-Formular als auch das
  28. Registrationsformular anzeigen.
  29. </para>
  30. </listitem>
  31. <listitem>
  32. <para>
  33. Die Angabe von ungültigen Anmeldedaten soll zur Anzeige des Login-Formulars
  34. führen.
  35. </para>
  36. </listitem>
  37. <listitem>
  38. <para>
  39. Das Ansehen der Anmeldedaten soll zu einer Umleitung zur Profilseite des
  40. Benutzers führen.
  41. </para>
  42. </listitem>
  43. <listitem>
  44. <para>
  45. Die Profilseite soll angepasst werden, um den Benutzernamen des Benutzers
  46. anzuzeigen.
  47. </para>
  48. </listitem>
  49. <listitem>
  50. <para>
  51. Authentifizierte Benutzer, welche die Loginseite besuchen, sollen zu ihrer
  52. Profilseite umgeleitet werden.
  53. </para>
  54. </listitem>
  55. <listitem>
  56. <para>
  57. Bei der Abmeldung soll ein Benutzer zur Loginseite umgeleitet werden.
  58. </para>
  59. </listitem>
  60. <listitem>
  61. <para>
  62. Mit ungültigen Daten soll die Registrierung fehlschlagen.
  63. </para>
  64. </listitem>
  65. </itemizedlist>
  66. <para>
  67. Wir können und sollten zusätzliche Tests definieren, aber diese reichen vorerst aus.
  68. </para>
  69. <para>
  70. Für unsere Anwendung definieren wir ein Plugin, 'Initialisieren' es, damit es bei
  71. <methodname>routeStartup()</methodname> läuft. Das erlaubt es uns, das Bootstrapping in
  72. einem OOP-Interface zu kapseln, was auch einen einfachen Weg bietet, um ein Callback zu
  73. ermöglichen. Schauen wir uns erstmals die Grundlagen dieser Klasse an:
  74. </para>
  75. <programlisting language="php"><![CDATA[
  76. class Bugapp_Plugin_Initialize extends Zend_Controller_Plugin_Abstract
  77. {
  78. /**
  79. * @var Zend_Config
  80. */
  81. protected static $_config;
  82. /**
  83. * @var string Aktuelle Umgebung
  84. */
  85. protected $_env;
  86. /**
  87. * @var Zend_Controller_Front
  88. */
  89. protected $_front;
  90. /**
  91. * @var string Pfad zum Root der Anwendung
  92. */
  93. protected $_root;
  94. /**
  95. * Constructor
  96. *
  97. * Umgebung, Root Pfad und Konfiguration initialisieren
  98. *
  99. * @param string $env
  100. * @param string|null $root
  101. * @return void
  102. */
  103. public function __construct($env, $root = null)
  104. {
  105. $this->_setEnv($env);
  106. if (null === $root) {
  107. $root = realpath(dirname(__FILE__) . '/../../../');
  108. }
  109. $this->_root = $root;
  110. $this->initPhpConfig();
  111. $this->_front = Zend_Controller_Front::getInstance();
  112. }
  113. /**
  114. * Route beginnen
  115. *
  116. * @return void
  117. */
  118. public function routeStartup(Zend_Controller_Request_Abstract $request)
  119. {
  120. $this->initDb();
  121. $this->initHelpers();
  122. $this->initView();
  123. $this->initPlugins();
  124. $this->initRoutes();
  125. $this->initControllers();
  126. }
  127. // Die Definition von Methoden würde hier folgen...
  128. }
  129. ]]></programlisting>
  130. <para>
  131. Das erlaubt es uns einen Bootstrap-Callback wie folgt zu erstellen:
  132. </para>
  133. <programlisting language="php"><![CDATA[
  134. class UserControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
  135. {
  136. public function appBootstrap()
  137. {
  138. $controller = $this->getFrontController();
  139. $controller->registerPlugin(
  140. new Bugapp_Plugin_Initialize('development')
  141. );
  142. }
  143. public function setUp()
  144. {
  145. $this->bootstrap = array($this, 'appBootstrap');
  146. parent::setUp();
  147. }
  148. // ...
  149. }
  150. ]]></programlisting>
  151. <para>
  152. Sobald das fertig ist, können wir unsere Tests schreiben. Was ist jedoch mit den
  153. Tests, die erfordern, dass der Benutzer angemeldet ist? Die einfache Lösung besteht darin,
  154. dass unsere Anwendungslogik das macht... und ein bisschen trickst, indem die Methoden
  155. <methodname>resetRequest()</methodname> und <methodname>resetResponse()</methodname>
  156. verwendet werden, die es uns erlauben eine andere Anfrage abzusetzen.
  157. </para>
  158. <programlisting language="php"><![CDATA[
  159. class UserControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
  160. {
  161. // ...
  162. public function loginUser($user, $password)
  163. {
  164. $this->request->setMethod('POST')
  165. ->setPost(array(
  166. 'username' => $user,
  167. 'password' => $password,
  168. ));
  169. $this->dispatch('/user/login');
  170. $this->assertRedirectTo('/user/view');
  171. $this->resetRequest()
  172. ->resetResponse();
  173. $this->request->setPost(array());
  174. // ...
  175. }
  176. // ...
  177. }
  178. ]]></programlisting>
  179. <para>
  180. Jetzt schreiben wir Tests:
  181. </para>
  182. <programlisting language="php"><![CDATA[
  183. class UserControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
  184. {
  185. // ...
  186. public function testCallWithoutActionShouldPullFromIndexAction()
  187. {
  188. $this->dispatch('/user');
  189. $this->assertController('user');
  190. $this->assertAction('index');
  191. }
  192. public function testLoginFormShouldContainLoginAndRegistrationForms()
  193. {
  194. $this->dispatch('/user');
  195. $this->assertQueryCount('form', 2);
  196. }
  197. public function testInvalidCredentialsShouldResultInRedisplayOfLoginForm()
  198. {
  199. $request = $this->getRequest();
  200. $request->setMethod('POST')
  201. ->setPost(array(
  202. 'username' => 'bogus',
  203. 'password' => 'reallyReallyBogus',
  204. ));
  205. $this->dispatch('/user/login');
  206. $this->assertNotRedirect();
  207. $this->assertQuery('form');
  208. }
  209. public function testValidLoginShouldRedirectToProfilePage()
  210. {
  211. $this->loginUser('foobar', 'foobar');
  212. }
  213. public function testAuthenticatedUserShouldHaveCustomizedProfilePage()
  214. {
  215. $this->loginUser('foobar', 'foobar');
  216. $this->request->setMethod('GET');
  217. $this->dispatch('/user/view');
  218. $this->assertNotRedirect();
  219. $this->assertQueryContentContains('h2', 'foobar');
  220. }
  221. public function
  222. testAuthenticatedUsersShouldBeRedirectedToProfileWhenVisitingLogin()
  223. {
  224. $this->loginUser('foobar', 'foobar');
  225. $this->request->setMethod('GET');
  226. $this->dispatch('/user');
  227. $this->assertRedirectTo('/user/view');
  228. }
  229. public function testUserShouldRedirectToLoginPageOnLogout()
  230. {
  231. $this->loginUser('foobar', 'foobar');
  232. $this->request->setMethod('GET');
  233. $this->dispatch('/user/logout');
  234. $this->assertRedirectTo('/user');
  235. }
  236. public function testRegistrationShouldFailWithInvalidData()
  237. {
  238. $data = array(
  239. 'username' => 'This will not work',
  240. 'email' => 'this is an invalid email',
  241. 'password' => 'Th1s!s!nv@l1d',
  242. 'passwordVerification' => 'wrong!',
  243. );
  244. $request = $this->getRequest();
  245. $request->setMethod('POST')
  246. ->setPost($data);
  247. $this->dispatch('/user/register');
  248. $this->assertNotRedirect();
  249. $this->assertQuery('form .errors');
  250. }
  251. }
  252. ]]></programlisting>
  253. <para>
  254. Es ist zu beachten, dass die Tests knapp sind und größtenteils nicht den
  255. aktuellen Inhalt suchen. Stattdessen suchen sie nach Teilen in der Anfrage --
  256. Anfrage Codes und Header sowie DOM-Knoten. Das erlaubt es schnell zu prüfen, dass die
  257. Strukturen wie erwartet sind -- und verhindern, dass die Tests jedesmal scheitern,
  258. wenn der Site neue Inhalte hinzugefügt werden.
  259. </para>
  260. <para>
  261. Es ist auch zu beachten, dass wir die Struktur des Dokuments in unseren Tests verwenden.
  262. Zum Beispiel suchen wir im letzten Test nach einer Form, die einen Knoten der Klasse
  263. "errors" hat; das erlaubt es uns lediglich auf das Vorhandensein von
  264. Form-Prüfungsfehlern zu testen und uns keine Sorgen darüber zu machen, warum spezielle
  265. Fehler überhaupt geworfen werden.
  266. </para>
  267. <para>
  268. Diese Anwendung <emphasis>könnte</emphasis> eine Datenbank verwenden. Wenn dem so ist,
  269. muss man wahrscheinlich einige Grundlagen ändern um sicherzustellen, dass die Datenbank am
  270. Anfang jedes Tests in einer unverfälschten, testbaren Konfiguration ist. PHPUnit bietet
  271. bereits Funktionalität um das sicherzustellen; <ulink
  272. url="http://www.phpunit.de/manual/3.4/en/database.html">Lesen Sie darüber in
  273. der PHPUnit-Dokumentation nach</ulink>. Wir empfehlen eine separate Datenbank für das
  274. Testen zu verwenden statt der Produktionsdatenbank und entweder eine SQLite-Datei oder
  275. eine Datenbank im Speicher zu verwenden, da beide Optionen sehr performant sind, keinen
  276. separaten Server benötigen und die meisten <acronym>SQL</acronym>-Syntax verwenden
  277. können.
  278. </para>
  279. </example>
  280. </sect2>