File: Zend_Test-PHPUnit-Examples.xml

package info (click to toggle)
zendframework 1.12.9%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: jessie-kfreebsd
  • size: 133,584 kB
  • sloc: xml: 1,311,829; php: 570,173; sh: 170; makefile: 125; sql: 121
file content (325 lines) | stat: -rw-r--r-- 10,708 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
<?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>