source: trunk/server/www/vendors/simpletest/docs/en/index.html @ 6

Last change on this file since 6 was 6, checked in by sander, 11 years ago

Added SimpleTest? test framework

File size: 23.2 KB
3<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
5        Download the Simple Test testing framework -
6        Unit tests and mock objects for PHP
7    </title>
8<link rel="stylesheet" type="text/css" href="docs.css" title="Styles">
11<div class="menu_back"><div class="menu">
12<span class="chosen">SimpleTest</span>
13                |
14                <a href="overview.html">Overview</a>
15                |
16                <a href="unit_test_documentation.html">Unit tester</a>
17                |
18                <a href="group_test_documentation.html">Group tests</a>
19                |
20                <a href="mock_objects_documentation.html">Mock objects</a>
21                |
22                <a href="partial_mocks_documentation.html">Partial mocks</a>
23                |
24                <a href="reporter_documentation.html">Reporting</a>
25                |
26                <a href="expectation_documentation.html">Expectations</a>
27                |
28                <a href="web_tester_documentation.html">Web tester</a>
29                |
30                <a href="form_testing_documentation.html">Testing forms</a>
31                |
32                <a href="authentication_documentation.html">Authentication</a>
33                |
34                <a href="browser_documentation.html">Scriptable browser</a>
36<h1>Simple Test for PHP</h1>
37        This page...
38        <ul>
40            <a href="#unit">Using unit tester</a>
41            with an example.
42        </li>
44            <a href="#group">Grouping tests</a>
45            for testing with one click.
46        </li>
48            <a href="#mock">Using mock objects</a>
49            to ease testing and gain tighter control.
50        </li>
52            <a href="#web">Testing web pages</a>
53            at the browser level.
54        </li>
56<div class="content">
59            <p>
60                The following assumes that you are familiar with the concept
61                of unit testing as well as the PHP web development language.
62                It is a guide for the impatient new user of
63                <a href="">SimpleTest</a>.
64                For fuller documentation, especially if you are new
65                to unit testing see the ongoing
66                <a href="unit_test_documentation.html">documentation</a>, and for
67                example test cases see the
68                <a href="">unit testing tutorial</a>.
69            </p>
71        <p><a class="target" name="unit"><h2>Using the tester quickly</h2></a></p>
72            <p>
73                Amongst software testing tools, a unit tester is the one
74                closest to the developer.
75                In the context of agile development the test code sits right
76                next to the source code as both are written simultaneously.
77                In this context SimpleTest aims to be a complete PHP developer
78                test solution and is called "Simple" because it
79                should be easy to use and extend.
80                It wasn't a good choice of name really.
81                It includes all of the typical functions you would expect from
82                <a href="">JUnit</a> and the
83                <a href="">PHPUnit</a>
84                ports, and includes
85                <a href="">mock objects</a>.
86            </p>
87            <p>
88                What makes this tool immediately useful to the PHP developer is the internal
89                web browser.
90                This allows tests that navigate web sites, fill in forms and test pages.
91                Being able to write these test in PHP means that it is easy to write
92                integrated tests.
93                An example might be confirming that a user was written to a database
94                after a signing up through the web site.
95            </p>
96            <p>
97                The quickest way to demonstrate SimpleTest is with an example.
98            </p>
99            <p>
100                Let us suppose we are testing a simple file logging class called
101                <span class="new_code">Log</span> in <em>classes/log.php</em>.
102                We start by creating a test script which we will call
103                <em>tests/log_test.php</em> and populate it as follows...
109class TestOfLogging extends <strong>UnitTestCase</strong> {
113                Here the <em>simpletest</em> folder is either local or in the path.
114                You would have to edit these locations depending on where you
115                unpacked the toolset.
116                The "autorun.php" file does more than just include the
117                SimpleTest files, it also runs our test for us.
118            </p>
119            <p>
120                The <span class="new_code">TestOfLogging</span> is our first test case and it's
121                currently empty.
122                Each test case is a class that extends one of the SimpleTet base classes
123                and we can have as many of these in the file as we want.
124            </p>
125            <p>
126                With three lines of scaffolding, and our <span class="new_code">Log</span> class
127                include, we have a test suite.
128                No tests though.
129            </p>
130            <p>
131                For our first test, we'll assume that the <span class="new_code">Log</span> class
132                takes the file name to write to in the constructor, and we have
133                a temporary folder in which to place this file...
139class TestOfLogging extends UnitTestCase {
140    function <strong>testLogCreatesNewFileOnFirstMessage()</strong> {
141        @unlink('/temp/test.log');
142        $log = new Log('/temp/test.log');
143        <strong>$this-&gt;assertFalse(file_exists('/temp/test.log'));</strong>
144        $log-&gt;message('Should write this to a file');
145        <strong>$this-&gt;assertTrue(file_exists('/temp/test.log'));</strong>
146    }
150                When a test case runs, it will search for any method that
151                starts with the string "test"
152                and execute that method.
153                If the method starts "test", it's a test.
154                Note the very long name <span class="new_code">testLogCreatesNewFileOnFirstMessage()</span>.
155                This is considered good style and makes the test output more readable.
156            </p>
157            <p>
158                We would normally have more than one test method in a test case,
159                but that's for later.
160            </p>
161            <p>
162                Assertions within the test methods trigger messages to the
163                test framework which displays the result immediately.
164                This immediate response is important, not just in the event
165                of the code causing a crash, but also so that
166                <span class="new_code">print</span> statements can display
167                their debugging content right next to the assertion concerned.
168            </p>
169            <p>
170                To see these results we have to actually run the tests.
171                No other code is necessary - we can just open the page
172                with our browser.
173            </p>
174            <p>
175                On failure the display looks like this...
176                <div class="demo">
177                    <h1>TestOfLogging</h1>
178                    <span class="fail">Fail</span>: testLogCreatesNewFileOnFirstMessage-&gt;True assertion failed.<br>
179                    <div style="padding: 8px; margin-top: 1em; background-color: red; color: white;">1/1 test cases complete.
180                    <strong>1</strong> passes and <strong>1</strong> fails.</div>
181                </div>
182                ...and if it passes like this...
183                <div class="demo">
184                    <h1>TestOfLogging</h1>
185                    <div style="padding: 8px; margin-top: 1em; background-color: green; color: white;">1/1 test cases complete.
186                    <strong>2</strong> passes and <strong>0</strong> fails.</div>
187                </div>
188                And if you get this...
189                <div class="demo">
190                    <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>
191                </div>
192                it means you're missing the <em>classes/Log.php</em> file that could look like...
195class Log {
196    function Log($file_path) {
197    }
199    function message() {
200    }
204                It's fun to write the code after the test.
205                More than fun even -
206                this system is called "Test Driven Development".
207            </p>
208            <p>
209                For more information about <span class="new_code">UnitTestCase</span>, see
210                the <a href="unit_test_documentation.html">unit test documentation</a>.
211            </p>
213        <p><a class="target" name="group"><h2>Building test suites</h2></a></p>
214            <p>
215                It is unlikely in a real application that we will only ever run
216                one test case.
217                This means that we need a way of grouping cases into a test
218                script that can, if need be, run every test for the application.
219            </p>
220            <p>
221                Our first step is to create a new file called <em>tests/all_tests.php</em>
222                and insert the following code...
227class AllTests extends <strong>TestSuite</strong> {
228    function AllTests() {
229        $this-&gt;TestSuite(<strong>'All tests'</strong>);
230        <strong>$this-&gt;addFile('log_test.php');</strong>
231    }
235                The "autorun" include allows our upcoming test suite
236                to be run just by invoking this script.
237            </p>
238            <p>
239                The <span class="new_code">TestSuite</span> subclass must chain it's constructor.
240                This limitation will be removed in future versions.
241            </p>
242            <p>
243                The method <span class="new_code">TestSuite::addFile()</span>
244                will include the test case file and read any new classes
245                that are descended from <span class="new_code">SimpleTestCase</span>.
246                <span class="new_code">UnitTestCase</span> is just one example of a class derived from
247                <span class="new_code">SimpleTestCase</span>, and you can create your own.
248                <span class="new_code">TestSuite::addFile()</span> can include other test suites.
249            </p>
250            <p>
251                The class will not be instantiated yet.
252                When the test suite runs it will construct each instance once
253                it reaches that test, then destroy it straight after.
254                This means that the constructor is run just before each run
255                of that test case, and the destructor is run before the next test case starts.
256            </p>
257            <p>
258                It is common to group test case code into superclasses which are not
259                supposed to run, but become the base classes of other tests.
260                For "autorun" to work properly the test case file should not blindly run
261                any other test case extensions that do not actually run tests.
262                This could result in extra test cases being counted during the test
263                run.
264                Hardly a major problem, but to avoid this inconvenience simply mark your
265                base class as <span class="new_code">abstract</span>.
266                SimpleTest won't run abstract classes.
267                If you are still using PHP4, then
268                a <span class="new_code">SimpleTestOptions::ignore()</span> directive
269                somewhere in the test case file will have the same effect.
270            </p>
271            <p>
272                Also, the test case file should not have been included
273                elsewhere or no cases will be added to this group test.
274                This would be a more serious error as if the test case classes are
275                already loaded by PHP the <span class="new_code">TestSuite::addFile()</span>
276                method will not detect them.
277            </p>
278            <p>
279                To display the results it is necessary only to invoke
280                <em>tests/all_tests.php</em> from the web server or the command line.
281            </p>
282            <p>
283                For more information about building test suites,
284                see the <a href="group_test_documentation.html">test suite documentation</a>.
285            </p>
287        <p><a class="target" name="mock"><h2>Using mock objects</h2></a></p>
288            <p>
289                Let's move further into the future and do something really complicated.
290            </p>
291            <p>
292                Assume that our logging class is tested and completed.
293                Assume also that we are testing another class that is
294                required to write log messages, say a
295                <span class="new_code">SessionPool</span>.
296                We want to test a method that will probably end up looking
297                like this...
299class SessionPool {
300    ...
301    function logIn($username) {
302        ...
303        $this-&gt;_log-&gt;message("User $username logged in.");
304        ...
305    }
306    ...
310                In the spirit of reuse, we are using our
311                <span class="new_code">Log</span> class.
312                A conventional test case might look like this...
319class <strong>TestOfSessionLogging</strong> extends UnitTestCase {
321    function setUp() {
322        <strong>@unlink('/temp/test.log');</strong>
323    }
325    function tearDown() {
326        <strong>@unlink('/temp/test.log');</strong>
327    }
329    function testLoggingInIsLogged() {
330        <strong>$log = new Log('/temp/test.log');
331        $session_pool = &amp;new SessionPool($log);
332        $session_pool-&gt;logIn('fred');</strong>
333        $messages = file('/temp/test.log');
334        $this-&gt;assertEqual($messages[0], "User fred logged in.<strong>\n</strong>");
335    }
339                We'll explain the <span class="new_code">setUp()</span> and <span class="new_code">tearDown()</span>
340                methods later.
341            </p>
342            <p>
343                This test case design is not all bad, but it could be improved.
344                We are spending time fiddling with log files which are
345                not part of our test.
346                We have created close ties with the <span class="new_code">Log</span> class and
347                this test.
348                What if we don't use files any more, but use ths
349                <em>syslog</em> library instead?
350                It means that our <span class="new_code">TestOfSessionLogging</span> test will
351                fail, even thouh it's not testing Logging.
352            </p>
353            <p>
354                It's fragile in smaller ways too.
355                Did you notice the extra carriage return in the message?
356                Was that added by the logger?
357                What if it also added a time stamp or other data?
358            </p>
359            <p>
360                The only part that we really want to test is that a particular
361                message was sent to the logger.
362                We can reduce coupling if we pass in a fake logging class
363                that simply records the message calls for testing, but
364                takes no action.
365                It would have to look exactly like our original though.
366            </p>
367            <p>
368                If the fake object doesn't write to a file then we save on deleting
369                the file before and after each test. We could save even more
370                test code if the fake object would kindly run the assertion for us.
371            <p>
372            </p>
373                Too good to be true?
374                We can create such an object easily...
383class TestOfSessionLogging extends UnitTestCase {
385    function testLoggingInIsLogged() {<strong>
386        $log = &amp;new MockLog();
387        $log-&gt;expectOnce('message', array('User fred logged in.'));</strong>
388        $session_pool = &amp;new SessionPool(<strong>$log</strong>);
389        $session_pool-&gt;logIn('fred');
390    }
394                The <span class="new_code">Mock::generate()</span> call code generated a new class
395                called <span class="new_code">MockLog</span>.
396                This looks like an identical clone, except that we can wire test code
397                to it.
398                That's what <span class="new_code">expectOnce()</span> does.
399                It says that if <span class="new_code">message()</span> is ever called on me, it had
400                better be with the parameter "User fred logged in.".
401            </p>
402            <p>
403                The test will be triggered when the call to
404                <span class="new_code">message()</span> is invoked on the
405                <span class="new_code">MockLog</span> object by <span class="new_code">SessionPool::logIn()</span> code.
406                The mock call will trigger a parameter comparison and then send the
407                resulting pass or fail event to the test display.
408                Wildcards can be included here too, so you don't have to test every parameter of
409                a call when you only want to test one.
410            </p>
411            <p>
412                If the mock reaches the end of the test case without the
413                method being called, the <span class="new_code">expectOnce()</span>
414                expectation will trigger a test failure.
415                In other words the mocks can detect the absence of
416                behaviour as well as the presence.
417            </p>
418            <p>
419                The mock objects in the SimpleTest suite can have arbitrary
420                return values set, sequences of returns, return values
421                selected according to the incoming arguments, sequences of
422                parameter expectations and limits on the number of times
423                a method is to be invoked.
424            </p>
425            <p>
426                For more information about mocking and stubbing, see the
427                <a href="mock_objects_documentation.html">mock objects documentation</a>.
428            </p>
430        <p><a class="target" name="web"><h2>Web page testing</h2></a></p>
431            <p>
432                One of the requirements of web sites is that they produce web
433                pages.
434                If you are building a project top-down and you want to fully
435                integrate testing along the way then you will want a way of
436                automatically navigating a site and examining output for
437                correctness.
438                This is the job of a web tester.
439            </p>
440            <p>
441                The web testing in SimpleTest is fairly primitive, as there is
442                no JavaScript.
443                Most other browser operations are simulated.
444            </p>
445            <p>
446                To give an idea here is a trivial example where a home
447                page is fetched, from which we navigate to an "about"
448                page and then test some client determined content.
454class TestOfAbout extends <strong>WebTestCase</strong> {
455    function testOurAboutPageGivesFreeReignToOurEgo() {
456        <strong>$this-&gt;get('http://test-server/index.php');
457        $this-&gt;click('About');
458        $this-&gt;assertTitle('About why we are so great');
459        $this-&gt;assertText('We are really great');</strong>
460    }
464                With this code as an acceptance test, you can ensure that
465                the content always meets the specifications of both the
466                developers, and the other project stakeholders.
467            </p>
468            <p>
469                You can navigate forms too...
475class TestOfRankings extends WebTestCase {
476    function testWeAreTopOfGoogle() {
477        $this-&gt;get('');
478        $this-&gt;setField('q', 'simpletest');
479        $this-&gt;click("I'm Feeling Lucky");
480        $this-&gt;assertTitle('SimpleTest - Unit Testing for PHP');
481    }
485                ...although this could violate Google's(tm) terms and conditions.
486            </p>
487            <p>
488                For more information about web testing, see the
489                <a href="browser_documentation.html">scriptable
490                browser documentation</a> and the
491                <a href="web_tester_documentation.html">WebTestCase</a>.
492            </p>
493            <p>
494                <a href=""><img src=";type=5" width="210" height="62" border="0" alt=" Logo"></a>
495            </p>
497    </div>
498        References and related information...
499        <ul>
501            <a href=";release_id=153280">Download PHP Simple Test</a>
502            from <a href="">SourceForge</a>.
503        </li>
505            The <a href="">developer's API for SimpleTest</a>
506            gives full detail on the classes and assertions available.
507        </li>
509<div class="menu_back"><div class="menu">
510<span class="chosen">SimpleTest</span>
511                |
512                <a href="overview.html">Overview</a>
513                |
514                <a href="unit_test_documentation.html">Unit tester</a>
515                |
516                <a href="group_test_documentation.html">Group tests</a>
517                |
518                <a href="mock_objects_documentation.html">Mock objects</a>
519                |
520                <a href="partial_mocks_documentation.html">Partial mocks</a>
521                |
522                <a href="reporter_documentation.html">Reporting</a>
523                |
524                <a href="expectation_documentation.html">Expectations</a>
525                |
526                <a href="web_tester_documentation.html">Web tester</a>
527                |
528                <a href="form_testing_documentation.html">Testing forms</a>
529                |
530                <a href="authentication_documentation.html">Authentication</a>
531                |
532                <a href="browser_documentation.html">Scriptable browser</a>
534<div class="copyright">
535            Copyright<br>Marcus Baker 2006
536        </div>
Note: See TracBrowser for help on using the repository browser.