aboutsummaryrefslogtreecommitdiff
path: root/vendors/simpletest/docs/en/index.html
blob: 03b6c5ceffa0522314022891a1cf49c89de5ad27 (plain)
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
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>
        Download the Simple Test testing framework -
        Unit tests and mock objects for PHP
    </title>
<link rel="stylesheet" type="text/css" href="docs.css" title="Styles">
</head>
<body>
<div class="menu_back"><div class="menu">
<span class="chosen">SimpleTest</span>
                |
                <a href="overview.html">Overview</a>
                |
                <a href="unit_test_documentation.html">Unit tester</a>
                |
                <a href="group_test_documentation.html">Group tests</a>
                |
                <a href="mock_objects_documentation.html">Mock objects</a>
                |
                <a href="partial_mocks_documentation.html">Partial mocks</a>
                |
                <a href="reporter_documentation.html">Reporting</a>
                |
                <a href="expectation_documentation.html">Expectations</a>
                |
                <a href="web_tester_documentation.html">Web tester</a>
                |
                <a href="form_testing_documentation.html">Testing forms</a>
                |
                <a href="authentication_documentation.html">Authentication</a>
                |
                <a href="browser_documentation.html">Scriptable browser</a>
</div></div>
<h1>Simple Test for PHP</h1>
        This page...
        <ul>
<li>
            <a href="#unit">Using unit tester</a>
            with an example.
        </li>
<li>
            <a href="#group">Grouping tests</a>
            for testing with one click.
        </li>
<li>
            <a href="#mock">Using mock objects</a>
            to ease testing and gain tighter control.
        </li>
<li>
            <a href="#web">Testing web pages</a>
            at the browser level.
        </li>
</ul>
<div class="content">
        
        
            <p>
                The following assumes that you are familiar with the concept
                of unit testing as well as the PHP web development language.
                It is a guide for the impatient new user of
                <a href="https://sourceforge.net/project/showfiles.php?group_id=76550">SimpleTest</a>.
                For fuller documentation, especially if you are new
                to unit testing see the ongoing
                <a href="unit_test_documentation.html">documentation</a>, and for
                example test cases see the
                <a href="http://www.lastcraft.com/first_test_tutorial.php">unit testing tutorial</a>.
            </p>
        
        <p><a class="target" name="unit"><h2>Using the tester quickly</h2></a></p>
            <p>
                Amongst software testing tools, a unit tester is the one
                closest to the developer.
                In the context of agile development the test code sits right
                next to the source code as both are written simultaneously.
                In this context SimpleTest aims to be a complete PHP developer
                test solution and is called "Simple" because it
                should be easy to use and extend.
                It wasn't a good choice of name really.
                It includes all of the typical functions you would expect from
                <a href="http://www.junit.org/">JUnit</a> and the
                <a href="http://sourceforge.net/projects/phpunit/">PHPUnit</a>
                ports, and includes
                <a href="http://www.mockobjects.com">mock objects</a>.
            </p>
            <p>
                What makes this tool immediately useful to the PHP developer is the internal
                web browser.
                This allows tests that navigate web sites, fill in forms and test pages.
                Being able to write these test in PHP means that it is easy to write
                integrated tests.
                An example might be confirming that a user was written to a database
                after a signing up through the web site.
            </p>
            <p>
                The quickest way to demonstrate SimpleTest is with an example.
            </p>
            <p>
                Let us suppose we are testing a simple file logging class called
                <span class="new_code">Log</span> in <em>classes/log.php</em>.
                We start by creating a test script which we will call
                <em>tests/log_test.php</em> and populate it as follows...
<pre>
&lt;?php
<strong>require_once('simpletest/autorun.php');</strong>
require_once('../classes/log.php');

class TestOfLogging extends <strong>UnitTestCase</strong> {
}
?&gt;
</pre>
                Here the <em>simpletest</em> folder is either local or in the path.
                You would have to edit these locations depending on where you
                unpacked the toolset.
                The "autorun.php" file does more than just include the
                SimpleTest files, it also runs our test for us.
            </p>
            <p>
                The <span class="new_code">TestOfLogging</span> is our first test case and it's
                currently empty.
                Each test case is a class that extends one of the SimpleTet base classes
                and we can have as many of these in the file as we want.
            </p>
            <p>
                With three lines of scaffolding, and our <span class="new_code">Log</span> class
                include, we have a test suite.
                No tests though.
            </p>
            <p>
                For our first test, we'll assume that the <span class="new_code">Log</span> class
                takes the file name to write to in the constructor, and we have
                a temporary folder in which to place this file...
<pre>
&lt;?php
require_once('simpletest/autorun.php');
require_once('../classes/log.php');

class TestOfLogging extends UnitTestCase {
    function <strong>testLogCreatesNewFileOnFirstMessage()</strong> {
        @unlink('/temp/test.log');
        $log = new Log('/temp/test.log');
        <strong>$this-&gt;assertFalse(file_exists('/temp/test.log'));</strong>
        $log-&gt;message('Should write this to a file');
        <strong>$this-&gt;assertTrue(file_exists('/temp/test.log'));</strong>
    }
}
?&gt;
</pre>
                When a test case runs, it will search for any method that
                starts with the string "test"
                and execute that method.
                If the method starts "test", it's a test.
                Note the very long name <span class="new_code">testLogCreatesNewFileOnFirstMessage()</span>.
                This is considered good style and makes the test output more readable.
            </p>
            <p>
                We would normally have more than one test method in a test case,
                but that's for later.
            </p>
            <p>
                Assertions within the test methods trigger messages to the
                test framework which displays the result immediately.
                This immediate response is important, not just in the event
                of the code causing a crash, but also so that
                <span class="new_code">print</span> statements can display
                their debugging content right next to the assertion concerned.
            </p>
            <p>
                To see these results we have to actually run the tests.
                No other code is necessary - we can just open the page
                with our browser.
            </p>
            <p>
                On failure the display looks like this...
                <div class="demo">
                    <h1>TestOfLogging</h1>
                    <span class="fail">Fail</span>: testLogCreatesNewFileOnFirstMessage-&gt;True assertion failed.<br>
                    <div style="padding: 8px; margin-top: 1em; background-color: red; color: white;">1/1 test cases complete.
                    <strong>1</strong> passes and <strong>1</strong> fails.</div>
                </div>
                ...and if it passes like this...
                <div class="demo">
                    <h1>TestOfLogging</h1>
                    <div style="padding: 8px; margin-top: 1em; background-color: green; color: white;">1/1 test cases complete.
                    <strong>2</strong> passes and <strong>0</strong> fails.</div>
                </div>
                And if you get this...
                <div class="demo">
                    <b>Fatal error</b>:  Failed opening required '../classes/log.php' (include_path='') in <b>/home/marcus/projects/lastcraft/tutorial_tests/Log/tests/log_test.php</b> on line <b>7</b>
                </div>
                it means you're missing the <em>classes/Log.php</em> file that could look like...
<pre>
&lt;?php<strong>
class Log {
    function Log($file_path) {
    }

    function message() {
    }
}</strong>
?&gt;
</pre>
                It's fun to write the code after the test.
                More than fun even -
                this system is called "Test Driven Development".
            </p>
            <p>
                For more information about <span class="new_code">UnitTestCase</span>, see
                the <a href="unit_test_documentation.html">unit test documentation</a>.
            </p>
        
        <p><a class="target" name="group"><h2>Building test suites</h2></a></p>
            <p>
                It is unlikely in a real application that we will only ever run
                one test case.
                This means that we need a way of grouping cases into a test
                script that can, if need be, run every test for the application.
            </p>
            <p>
                Our first step is to create a new file called <em>tests/all_tests.php</em>
                and insert the following code...
<pre>
&lt;?php
<strong>require_once('simpletest/autorun.php');</strong>

class AllTests extends <strong>TestSuite</strong> {
    function AllTests() {
        $this-&gt;TestSuite(<strong>'All tests'</strong>);
        <strong>$this-&gt;addFile('log_test.php');</strong>
    }
}
?&gt;
</pre>
                The "autorun" include allows our upcoming test suite
                to be run just by invoking this script.
            </p>
            <p>
                The <span class="new_code">TestSuite</span> subclass must chain it's constructor.
                This limitation will be removed in future versions.
            </p>
            <p>
                The method <span class="new_code">TestSuite::addFile()</span>
                will include the test case file and read any new classes
                that are descended from <span class="new_code">SimpleTestCase</span>.
                <span class="new_code">UnitTestCase</span> is just one example of a class derived from
                <span class="new_code">SimpleTestCase</span>, and you can create your own.
                <span class="new_code">TestSuite::addFile()</span> can include other test suites.
            </p>
            <p>
                The class will not be instantiated yet.
                When the test suite runs it will construct each instance once
                it reaches that test, then destroy it straight after.
                This means that the constructor is run just before each run
                of that test case, and the destructor is run before the next test case starts.
            </p>
            <p>
                It is common to group test case code into superclasses which are not
                supposed to run, but become the base classes of other tests.
                For "autorun" to work properly the test case file should not blindly run
                any other test case extensions that do not actually run tests.
                This could result in extra test cases being counted during the test
                run.
                Hardly a major problem, but to avoid this inconvenience simply mark your
                base class as <span class="new_code">abstract</span>.
                SimpleTest won't run abstract classes.
                If you are still using PHP4, then
                a <span class="new_code">SimpleTestOptions::ignore()</span> directive
                somewhere in the test case file will have the same effect.
            </p>
            <p>
                Also, the test case file should not have been included
                elsewhere or no cases will be added to this group test.
                This would be a more serious error as if the test case classes are
                already loaded by PHP the <span class="new_code">TestSuite::addFile()</span>
                method will not detect them.
            </p>
            <p>
                To display the results it is necessary only to invoke
                <em>tests/all_tests.php</em> from the web server or the command line.
            </p>
            <p>
                For more information about building test suites,
                see the <a href="group_test_documentation.html">test suite documentation</a>.
            </p>
        
        <p><a class="target" name="mock"><h2>Using mock objects</h2></a></p>
            <p>
                Let's move further into the future and do something really complicated.
            </p>
            <p>
                Assume that our logging class is tested and completed.
                Assume also that we are testing another class that is
                required to write log messages, say a
                <span class="new_code">SessionPool</span>.
                We want to test a method that will probably end up looking
                like this...
<pre><strong>
class SessionPool {
    ...
    function logIn($username) {
        ...
        $this-&gt;_log-&gt;message("User $username logged in.");
        ...
    }
    ...
}
</strong>
</pre>
                In the spirit of reuse, we are using our
                <span class="new_code">Log</span> class.
                A conventional test case might look like this...
<pre>
&lt;?php
require_once('simpletest/autorun.php');
require_once('../classes/log.php');
<strong>require_once('../classes/session_pool.php');</strong>

class <strong>TestOfSessionLogging</strong> extends UnitTestCase {
    
    function setUp() {
        <strong>@unlink('/temp/test.log');</strong>
    }
    
    function tearDown() {
        <strong>@unlink('/temp/test.log');</strong>
    }
    
    function testLoggingInIsLogged() {
        <strong>$log = new Log('/temp/test.log');
        $session_pool = &amp;new SessionPool($log);
        $session_pool-&gt;logIn('fred');</strong>
        $messages = file('/temp/test.log');
        $this-&gt;assertEqual($messages[0], "User fred logged in.<strong>\n</strong>");
    }
}
?&gt;
</pre>
                We'll explain the <span class="new_code">setUp()</span> and <span class="new_code">tearDown()</span>
                methods later.
            </p>
            <p>
                This test case design is not all bad, but it could be improved.
                We are spending time fiddling with log files which are
                not part of our test.
                We have created close ties with the <span class="new_code">Log</span> class and
                this test.
                What if we don't use files any more, but use ths
                <em>syslog</em> library instead?
                It means that our <span class="new_code">TestOfSessionLogging</span> test will
                fail, even thouh it's not testing Logging.
            </p>
            <p>
                It's fragile in smaller ways too.
                Did you notice the extra carriage return in the message?
                Was that added by the logger?
                What if it also added a time stamp or other data?
            </p>
            <p>
                The only part that we really want to test is that a particular
                message was sent to the logger.
                We can reduce coupling if we pass in a fake logging class
                that simply records the message calls for testing, but
                takes no action.
                It would have to look exactly like our original though.
            </p>
            <p>
                If the fake object doesn't write to a file then we save on deleting
                the file before and after each test. We could save even more
                test code if the fake object would kindly run the assertion for us.
            <p>
            </p>
                Too good to be true?
                We can create such an object easily...
<pre>
&lt;?php
require_once('simpletest/autorun.php');
require_once('../classes/log.php');
require_once('../classes/session_pool.php');

<strong>Mock::generate('Log');</strong>

class TestOfSessionLogging extends UnitTestCase {
    
    function testLoggingInIsLogged() {<strong>
        $log = &amp;new MockLog();
        $log-&gt;expectOnce('message', array('User fred logged in.'));</strong>
        $session_pool = &amp;new SessionPool(<strong>$log</strong>);
        $session_pool-&gt;logIn('fred');
    }
}
?&gt;
</pre>
                The <span class="new_code">Mock::generate()</span> call code generated a new class
                called <span class="new_code">MockLog</span>.
                This looks like an identical clone, except that we can wire test code
                to it.
                That's what <span class="new_code">expectOnce()</span> does.
                It says that if <span class="new_code">message()</span> is ever called on me, it had
                better be with the parameter "User fred logged in.".
            </p>
            <p>
                The test will be triggered when the call to
                <span class="new_code">message()</span> is invoked on the
                <span class="new_code">MockLog</span> object by <span class="new_code">SessionPool::logIn()</span> code.
                The mock call will trigger a parameter comparison and then send the
                resulting pass or fail event to the test display.
                Wildcards can be included here too, so you don't have to test every parameter of
                a call when you only want to test one.
            </p>
            <p>
                If the mock reaches the end of the test case without the
                method being called, the <span class="new_code">expectOnce()</span>
                expectation will trigger a test failure.
                In other words the mocks can detect the absence of
                behaviour as well as the presence.
            </p>
            <p>
                The mock objects in the SimpleTest suite can have arbitrary
                return values set, sequences of returns, return values
                selected according to the incoming arguments, sequences of
                parameter expectations and limits on the number of times
                a method is to be invoked.
            </p>
            <p>
                For more information about mocking and stubbing, see the
                <a href="mock_objects_documentation.html">mock objects documentation</a>.
            </p>
        
        <p><a class="target" name="web"><h2>Web page testing</h2></a></p>
            <p>
                One of the requirements of web sites is that they produce web
                pages.
                If you are building a project top-down and you want to fully
                integrate testing along the way then you will want a way of
                automatically navigating a site and examining output for
                correctness.
                This is the job of a web tester.
            </p>
            <p>
                The web testing in SimpleTest is fairly primitive, as there is
                no JavaScript.
                Most other browser operations are simulated.
            </p>
            <p>
                To give an idea here is a trivial example where a home
                page is fetched, from which we navigate to an "about"
                page and then test some client determined content.
<pre>
&lt;?php
require_once('simpletest/autorun.php');
<strong>require_once('simpletest/web_tester.php');</strong>

class TestOfAbout extends <strong>WebTestCase</strong> {
    function testOurAboutPageGivesFreeReignToOurEgo() {
        <strong>$this-&gt;get('http://test-server/index.php');
        $this-&gt;click('About');
        $this-&gt;assertTitle('About why we are so great');
        $this-&gt;assertText('We are really great');</strong>
    }
}
?&gt;
</pre>
                With this code as an acceptance test, you can ensure that
                the content always meets the specifications of both the
                developers, and the other project stakeholders.
            </p>
            <p>
                You can navigate forms too...
<pre>
&lt;?php
require_once('simpletest/autorun.php');
require_once('simpletest/web_tester.php');

class TestOfRankings extends WebTestCase {
    function testWeAreTopOfGoogle() {
        $this-&gt;get('http://google.com/');
        $this-&gt;setField('q', 'simpletest');
        $this-&gt;click("I'm Feeling Lucky");
        $this-&gt;assertTitle('SimpleTest - Unit Testing for PHP');
    }
}
?&gt;
</pre>
                ...although this could violate Google's(tm) terms and conditions.
            </p>
            <p>
                For more information about web testing, see the
                <a href="browser_documentation.html">scriptable
                browser documentation</a> and the
                <a href="web_tester_documentation.html">WebTestCase</a>.
            </p>
            <p>
                <a href="http://sourceforge.net/projects/simpletest/"><img src="http://sourceforge.net/sflogo.php?group_id=76550&amp;type=5" width="210" height="62" border="0" alt="SourceForge.net Logo"></a>
            </p>
        
    </div>
        References and related information...
        <ul>
<li>
            <a href="https://sourceforge.net/project/showfiles.php?group_id=76550&amp;release_id=153280">Download PHP Simple Test</a>
            from <a href="http://sourceforge.net/projects/simpletest/">SourceForge</a>.
        </li>
<li>
            The <a href="http://simpletest.org/api/">developer's API for SimpleTest</a>
            gives full detail on the classes and assertions available.
        </li>
</ul>
<div class="menu_back"><div class="menu">
<span class="chosen">SimpleTest</span>
                |
                <a href="overview.html">Overview</a>
                |
                <a href="unit_test_documentation.html">Unit tester</a>
                |
                <a href="group_test_documentation.html">Group tests</a>
                |
                <a href="mock_objects_documentation.html">Mock objects</a>
                |
                <a href="partial_mocks_documentation.html">Partial mocks</a>
                |
                <a href="reporter_documentation.html">Reporting</a>
                |
                <a href="expectation_documentation.html">Expectations</a>
                |
                <a href="web_tester_documentation.html">Web tester</a>
                |
                <a href="form_testing_documentation.html">Testing forms</a>
                |
                <a href="authentication_documentation.html">Authentication</a>
                |
                <a href="browser_documentation.html">Scriptable browser</a>
</div></div>
<div class="copyright">
            Copyright<br>Marcus Baker 2006
        </div>
</body>
</html>