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

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

Added SimpleTest? test framework

File size: 32.3 KB
3<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
4<title>SimpleTest for PHP mock objects documentation</title>
5<link rel="stylesheet" type="text/css" href="docs.css" title="Styles">
8<div class="menu_back"><div class="menu">
9<a href="index.html">SimpleTest</a>
10                |
11                <a href="overview.html">Overview</a>
12                |
13                <a href="unit_test_documentation.html">Unit tester</a>
14                |
15                <a href="group_test_documentation.html">Group tests</a>
16                |
17                <span class="chosen">Mock objects</span>
18                |
19                <a href="partial_mocks_documentation.html">Partial mocks</a>
20                |
21                <a href="reporter_documentation.html">Reporting</a>
22                |
23                <a href="expectation_documentation.html">Expectations</a>
24                |
25                <a href="web_tester_documentation.html">Web tester</a>
26                |
27                <a href="form_testing_documentation.html">Testing forms</a>
28                |
29                <a href="authentication_documentation.html">Authentication</a>
30                |
31                <a href="browser_documentation.html">Scriptable browser</a>
33<h1>Mock objects documentation</h1>
34        This page...
35        <ul>
37            <a href="#what">What are mock objects?</a>
38        </li>
40            <a href="#creation">Creating mock objects</a>.
41        </li>
43            <a href="#stub">Mocks as actors</a> or stubs.
44        </li>
46            <a href="#expectations">Mocks as critics</a> with expectations.
47        </li>
49            <a href="#approaches">Other approaches</a> including mock libraries.
50        </li>
52<div class="content">
53        <p><a class="target" name="what"><h2>What are mock objects?</h2></a></p>
54            <p>
55                Mock objects have two roles during a test case: actor and critic.
56            </p>
57            <p>
58                The actor behaviour is to simulate objects that are difficult to
59                set up or time consuming to set up for a test.
60                The classic example is a database connection.
61                Setting up a test database at the start of each test would slow
62                testing to a crawl and would require the installation of the
63                database engine and test data on the test machine.
64                If we can simulate the connection and return data of our
65                choosing we not only win on the pragmatics of testing, but can
66                also feed our code spurious data to see how it responds.
67                We can simulate databases being down or other extremes
68                without having to create a broken database for real.
69                In other words, we get greater control of the test environment.
70            </p>
71            <p>
72                If mock objects only behaved as actors they would simply be
73                known as server stubs.
74                This was originally a pattern named by Robert Binder (Testing
75                object-oriented systems: models, patterns, and tools,
76                Addison-Wesley) in 1999.
77            </p>
78            <p>
79                A server stub is a simulation of an object or component.
80                It should exactly replace a component in a system for test
81                or prototyping purposes, but remain lightweight.
82                This allows tests to run more quickly, or if the simulated
83                class has not been written, to run at all.
84            </p>
85            <p>
86                However, the mock objects not only play a part (by supplying chosen
87                return values on demand) they are also sensitive to the
88                messages sent to them (via expectations).
89                By setting expected parameters for a method call they act
90                as a guard that the calls upon them are made correctly.
91                If expectations are not met they save us the effort of
92                writing a failed test assertion by performing that duty on our
93                behalf.
94            </p>
95            <p>
96                In the case of an imaginary database connection they can
97                test that the query, say SQL, was correctly formed by
98                the object that is using the connection.
99                Set them up with fairly tight expectations and you will
100                hardly need manual assertions at all.
101            </p>
103        <p><a class="target" name="creation"><h2>Creating mock objects</h2></a></p>
104            <p>
105                In the same way that we create server stubs, all we need is an
106                existing class, say a database connection that looks like this...
108<strong>class DatabaseConnection {
109    function DatabaseConnection() {
110    }
112    function query() {
113    }
115    function selectQuery() {
116    }
119                The class does not need to have been implemented yet.
120                To create a mock version of the class we need to include the
121                mock object library and run the generator...
129                This generates a clone class called
130                <span class="new_code">MockDatabaseConnection</span>.
131                We can now create instances of the new class within
132                our test case...
140class MyTestCase extends UnitTestCase {
142    function testSomething() {
143        $connection = &amp;new MockDatabaseConnection();
144    }
147                Unlike the generated stubs the mock constructor needs a reference
148                to the test case so that it can dispatch passes and failures while
149                checking its expectations.
150                This means that mock objects can only be used within test cases.
151                Despite this their extra power means that stubs are hardly ever used
152                if mocks are available.
153            </p>
154            <p>
155                <a class="target" name="stub"><h2>Mocks as actors</h2></a>
156            </p>
157            <p>
158                The mock version of a class has all the methods of the original,
159                so that operations like
160                <span class="new_code">$connection-&gt;query()</span> are still
161                legal.
162                The return value will be <span class="new_code">null</span>,
163                but we can change that with...
165<strong>$connection-&gt;setReturnValue('query', 37)</strong>
167                Now every time we call
168                <span class="new_code">$connection-&gt;query()</span> we get
169                the result of 37.
170                We can set the return value to anything, say a hash of
171                imaginary database results or a list of persistent objects.
172                Parameters are irrelevant here, we always get the same
173                values back each time once they have been set up this way.
174                That may not sound like a convincing replica of a
175                database connection, but for the half a dozen lines of
176                a test method it is usually all you need.
177            </p>
178            <p>
179                We can also add extra methods to the mock when generating it
180                and choose our own class name...
182<strong>Mock::generate('DatabaseConnection', 'MyMockDatabaseConnection', array('setOptions'));</strong>
184                Here the mock will behave as if the <span class="new_code">setOptions()</span>
185                existed in the original class.
186                This is handy if a class has used the PHP <span class="new_code">overload()</span>
187                mechanism to add dynamic methods.
188                You can create a special mock to simulate this situation.
189            </p>
190            <p>
191                Things aren't always that simple though.
192                One common problem is iterators, where constantly returning
193                the same value could cause an endless loop in the object
194                being tested.
195                For these we need to set up sequences of values.
196                Let's say we have a simple iterator that looks like this...
198class Iterator {
199    function Iterator() {
200    }
202    function next() {
203    }
206                This is about the simplest iterator you could have.
207                Assuming that this iterator only returns text until it
208                reaches the end, when it returns false, we can simulate it
209                with...
213class IteratorTest extends UnitTestCase() {
215    function testASequence() {<strong>
216        $iterator = &amp;new MockIterator();
217        $iterator-&gt;setReturnValue('next', false);
218        $iterator-&gt;setReturnValueAt(0, 'next', 'First string');
219        $iterator-&gt;setReturnValueAt(1, 'next', 'Second string');</strong>
220        ...
221    }
224                When <span class="new_code">next()</span> is called on the
225                mock iterator it will first return "First string",
226                on the second call "Second string" will be returned
227                and on any other call <span class="new_code">false</span> will
228                be returned.
229                The sequenced return values take precedence over the constant
230                return value.
231                The constant one is a kind of default if you like.
232            </p>
233            <p>
234                Another tricky situation is an overloaded
235                <span class="new_code">get()</span> operation.
236                An example of this is an information holder with name/value pairs.
237                Say we have a configuration class like...
239class Configuration {
240    function Configuration() {
241    }
243    function getValue($key) {
244    }
247                This is a classic situation for using mock objects as
248                actual configuration will vary from machine to machine,
249                hardly helping the reliability of our tests if we use it
250                directly.
251                The problem though is that all the data comes through the
252                <span class="new_code">getValue()</span> method and yet
253                we want different results for different keys.
254                Luckily the mocks have a filter system...
256<strong>$config = &amp;new MockConfiguration();
257$config-&gt;setReturnValue('getValue', 'primary', array('db_host'));
258$config-&gt;setReturnValue('getValue', 'admin', array('db_user'));
259$config-&gt;setReturnValue('getValue', 'secret', array('db_password'));</strong>
261                The extra parameter is a list of arguments to attempt
262                to match.
263                In this case we are trying to match only one argument which
264                is the look up key.
265                Now when the mock object has the
266                <span class="new_code">getValue()</span> method invoked
267                like this...
271       will return "admin".
272                It finds this by attempting to match the calling arguments
273                to its list of returns one after another until
274                a complete match is found.
275            </p>
276            <p>
277                You can set a default argument argument like so...
279$config-&gt;setReturnValue('getValue', false, array('*'));</strong>
281                This is not the same as setting the return value without
282                any argument requirements like this...
284$config-&gt;setReturnValue('getValue', false);</strong>
286                In the first case it will accept any single argument,
287                but exactly one is required.
288                In the second case any number of arguments will do and
289                it acts as a catchall after all other matches.
290                Note that if we add further single parameter options after
291                the wildcard in the first case, they will be ignored as the wildcard
292                will match first.
293                With complex parameter lists the ordering could be important
294                or else desired matches could be masked by earlier wildcard
295                ones.
296                Declare the most specific matches first if you are not sure.
297            </p>
298            <p>
299                There are times when you want a specific object to be
300                dished out by the mock rather than a copy.
301                The PHP4 copy semantics force us to use a different method
302                for this.
303                You might be simulating a container for example...
305class Thing {
308class Vector {
309    function Vector() {
310    }
312    function get($index) {
313    }
316                In this case you can set a reference into the mock's
317                return list...
319$thing = &amp;new Thing();<strong>
320$vector = &amp;new MockVector();
321$vector-&gt;setReturnReference('get', $thing, array(12));</strong>
323                With this arrangement you know that every time
324                <span class="new_code">$vector-&gt;get(12)</span> is
325                called it will return the same
326                <span class="new_code">$thing</span> each time.
327                This is compatible with PHP5 as well.
328            </p>
329            <p>
330                These three factors, timing, parameters and whether to copy,
331                can be combined orthogonally.
332                For example...
334$complex = &amp;new MockComplexThing();
335$stuff = &amp;new Stuff();<strong>
336$complex-&gt;setReturnReferenceAt(3, 'get', $stuff, array('*', 1));</strong>
338                This will return the <span class="new_code">$stuff</span> only on the third
339                call and only if two parameters were set the second of
340                which must be the integer 1.
341                That should cover most simple prototyping situations.
342            </p>
343            <p>
344                A final tricky case is one object creating another, known
345                as a factory pattern.
346                Suppose that on a successful query to our imaginary
347                database, a result set is returned as an iterator with
348                each call to <span class="new_code">next()</span> giving
349                one row until false.
350                This sounds like a simulation nightmare, but in fact it can all
351                be mocked using the mechanics above.
352            </p>
353            <p>
354                Here's how...
359class DatabaseTest extends UnitTestCase {
361    function testUserFinder() {<strong>
362        $result = &amp;new MockResultIterator();
363        $result-&gt;setReturnValue('next', false);
364        $result-&gt;setReturnValueAt(0, 'next', array(1, 'tom'));
365        $result-&gt;setReturnValueAt(1, 'next', array(3, 'dick'));
366        $result-&gt;setReturnValueAt(2, 'next', array(6, 'harry'));
368        $connection = &amp;new MockDatabaseConnection();
369        $connection-&gt;setReturnValue('query', false);
370        $connection-&gt;setReturnReference(
371                'query',
372                $result,
373                array('select id, name from users'));</strong>
375        $finder = &amp;new UserFinder($connection);
376        $this-&gt;assertIdentical(
377                $finder-&gt;findNames(),
378                array('tom', 'dick', 'harry'));
379    }
382                Now only if our
383                <span class="new_code">$connection</span> is called with the correct
384                <span class="new_code">query()</span> will the
385                <span class="new_code">$result</span> be returned that is
386                itself exhausted after the third call to <span class="new_code">next()</span>.
387                This should be enough
388                information for our <span class="new_code">UserFinder</span> class,
389                the class actually
390                being tested here, to come up with goods.
391                A very precise test and not a real database in sight.
392            </p>
394        <p><a class="target" name="expectations"><h2>Mocks as critics</h2></a></p>
395            <p>
396                Although the server stubs approach insulates your tests from
397                real world disruption, it is only half the benefit.
398                You can have the class under test receiving the required
399                messages, but is your new class sending correct ones?
400                Testing this can get messy without a mock objects library.
401            </p>
402            <p>
403                By way of example, suppose we have a
404                <span class="new_code">SessionPool</span> class that we
405                want to add logging to.
406                Rather than grow the original class into something more
407                complicated, we want to add this behaviour with a decorator (GOF).
408                The <span class="new_code">SessionPool</span> code currently looks
409                like this...
411<strong>class SessionPool {
412    function SessionPool() {
413        ...
414    }
416    function &amp;findSession($cookie) {
417        ...
418    }
419    ...
422class Session {
423    ...
426                While our logging code looks like this...
429class Log {
430    function Log() {
431        ...
432    }
434    function message() {
435        ...
436    }
439class LoggingSessionPool {
440    function LoggingSessionPool(&amp;$session_pool, &amp;$log) {
441        ...
442    }
444    function &amp;findSession($cookie) {
445        ...
446    }
447    ...
450                Out of all of this, the only class we want to test here
451                is the <span class="new_code">LoggingSessionPool</span>.
452                In particular we would like to check that the
453                <span class="new_code">findSession()</span> method is
454                called with the correct session ID in the cookie and that
455                it sent the message "Starting session $cookie"
456                to the logger.
457            </p>
458            <p>
459                Despite the fact that we are testing only a few lines of
460                production code, here is what we would have to do in a
461                conventional test case:
462                <ol>
463                    <li>Create a log object.</li>
464                    <li>Set a directory to place the log file.</li>
465                    <li>Set the directory permissions so we can write the log.</li>
466                    <li>Create a <span class="new_code">SessionPool</span> object.</li>
467                    <li>Hand start a session, which probably does lot's of things.</li>
468                    <li>Invoke <span class="new_code">findSession()</span>.</li>
469                    <li>Read the new Session ID (hope there is an accessor!).</li>
470                    <li>Raise a test assertion to confirm that the ID matches the cookie.</li>
471                    <li>Read the last line of the log file.</li>
472                    <li>Pattern match out the extra logging timestamps, etc.</li>
473                    <li>Assert that the session message is contained in the text.</li>
474                </ol>
475                It is hardly surprising that developers hate writing tests
476                when they are this much drudgery.
477                To make things worse, every time the logging format changes or
478                the method of creating new sessions changes, we have to rewrite
479                parts of this test even though this test does not officially
480                test those parts of the system.
481                We are creating headaches for the writers of these other classes.
482            </p>
483            <p>
484                Instead, here is the complete test method using mock object magic...
490class LoggingSessionPoolTest extends UnitTestCase {
491    ...
492    function testFindSessionLogging() {<strong>
493        $session = &amp;new MockSession();
494        $pool = &amp;new MockSessionPool();
495        $pool-&gt;setReturnReference('findSession', $session);
496        $pool-&gt;expectOnce('findSession', array('abc'));
498        $log = &amp;new MockLog();
499        $log-&gt;expectOnce('message', array('Starting session abc'));
501        $logging_pool = &amp;new LoggingSessionPool($pool, $log);
502        $this-&gt;assertReference($logging_pool-&gt;findSession('abc'), $session);</strong>
503    }
506                We start by creating a dummy session.
507                We don't have to be too fussy about this as the check
508                for which session we want is done elsewhere.
509                We only need to check that it was the same one that came
510                from the session pool.
511            </p>
512            <p>
513                <span class="new_code">findSession()</span> is a factory
514                method the simulation of which is described <a href="#stub">above</a>.
515                The point of departure comes with the first
516                <span class="new_code">expectOnce()</span> call.
517                This line states that whenever
518                <span class="new_code">findSession()</span> is invoked on the
519                mock, it will test the incoming arguments.
520                If it receives the single argument of a string "abc"
521                then a test pass is sent to the unit tester, otherwise a fail is
522                generated.
523                This was the part where we checked that the right session was asked for.
524                The argument list follows the same format as the one for setting
525                return values.
526                You can have wildcards and sequences and the order of
527                evaluation is the same.
528            </p>
529            <p>
530                We use the same pattern to set up the mock logger.
531                We tell it that it should have
532                <span class="new_code">message()</span> invoked
533                once only with the argument "Starting session abc".
534                By testing the calling arguments, rather than the logger output,
535                we insulate the test from any display changes in the logger.
536            </p>
537            <p>
538                We start to run our tests when we create the new
539                <span class="new_code">LoggingSessionPool</span> and feed
540                it our preset mock objects.
541                Everything is now under our control.
542            </p>
543            <p>
544                This is still quite a bit of test code, but the code is very
545                strict.
546                If it still seems rather daunting there is a lot less of it
547                than if we tried this without mocks and this particular test,
548                interactions rather than output, is always more work to set
549                up.
550                More often you will be testing more complex situations without
551                needing this level or precision.
552                Also some of this can be refactored into a test case
553                <span class="new_code">setUp()</span> method.
554            </p>
555            <p>
556                Here is the full list of expectations you can set on a mock object
557                in <a href="">SimpleTest</a>...
558                <table>
560                    <tr>
562<th>Needs <span class="new_code">tally()</span>
565                    </thead>
568                        <td><span class="new_code">expect($method, $args)</span></td>
569                        <td style="text-align: center">No</td>
570                    </tr>
571                    <tr>
572                        <td><span class="new_code">expectAt($timing, $method, $args)</span></td>
573                        <td style="text-align: center">No</td>
574                    </tr>
575                    <tr>
576                        <td><span class="new_code">expectCallCount($method, $count)</span></td>
577                        <td style="text-align: center">Yes</td>
578                    </tr>
579                    <tr>
580                        <td><span class="new_code">expectMaximumCallCount($method, $count)</span></td>
581                        <td style="text-align: center">No</td>
582                    </tr>
583                    <tr>
584                        <td><span class="new_code">expectMinimumCallCount($method, $count)</span></td>
585                        <td style="text-align: center">Yes</td>
586                    </tr>
587                    <tr>
588                        <td><span class="new_code">expectNever($method)</span></td>
589                        <td style="text-align: center">No</td>
590                    </tr>
591                    <tr>
592                        <td><span class="new_code">expectOnce($method, $args)</span></td>
593                        <td style="text-align: center">Yes</td>
594                    </tr>
595                    <tr>
596                        <td><span class="new_code">expectAtLeastOnce($method, $args)</span></td>
597                        <td style="text-align: center">Yes</td>
598                    </tr>
599                </tbody>
601                Where the parameters are...
602                <dl>
603                    <dt class="new_code">$method</dt>
604                    <dd>The method name, as a string, to apply the condition to.</dd>
605                    <dt class="new_code">$args</dt>
606                    <dd>
607                        The arguments as a list. Wildcards can be included in the same
608                        manner as for <span class="new_code">setReturn()</span>.
609                        This argument is optional for <span class="new_code">expectOnce()</span>
610                        and <span class="new_code">expectAtLeastOnce()</span>.
611                    </dd>
612                    <dt class="new_code">$timing</dt>
613                    <dd>
614                        The only point in time to test the condition.
615                        The first call starts at zero.
616                    </dd>
617                    <dt class="new_code">$count</dt>
618                    <dd>The number of calls expected.</dd>
619                </dl>
620                The method <span class="new_code">expectMaximumCallCount()</span>
621                is slightly different in that it will only ever generate a failure.
622                It is silent if the limit is never reached.
623            </p>
624            <p>
625                Also if you have juste one call in your test, make sure you're using
626                <span class="new_code">expectOnce</span>.<br>
627                Using <span class="new_code">$mocked-&gt;expectAt(0, 'method', 'args);</span>
628                on its own will not be catched :
629                checking the arguments and the overall call count
630                are currently independant.
631            </p>
632            <p>
633                Like the assertions within test cases, all of the expectations
634                can take a message override as an extra parameter.
635                Also the original failure message can be embedded in the output
636                as "%s".
637            </p>
639        <p><a class="target" name="approaches"><h2>Other approaches</h2></a></p>
640            <p>
641                There are three approaches to creating mocks including the one
642                that SimpleTest employs.
643                Coding them by hand using a base class, generating them to
644                a file and dynamically generating them on the fly.
645            </p>
646            <p>
647                Mock objects generated with <a href="simple_test.html">SimpleTest</a>
648                are dynamic.
649                They are created at run time in memory, using
650                <span class="new_code">eval()</span>, rather than written
651                out to a file.
652                This makes the mocks easy to create, a one liner,
653                especially compared with hand
654                crafting them in a parallel class hierarchy.
655                The problem is that the behaviour is usually set up in the tests
656                themselves.
657                If the original objects change the mock versions
658                that the tests rely on can get out of sync.
659                This can happen with the parallel hierarchy approach as well,
660                but is far more quickly detected.
661            </p>
662            <p>
663                The solution, of course, is to add some real integration
664                tests.
665                You don't need very many and the convenience gained
666                from the mocks more than outweighs the small amount of
667                extra testing.
668                You cannot trust code that was only tested with mocks.
669            </p>
670            <p>
671                If you are still determined to build static libraries of mocks
672                because you want to simulate very specific behaviour, you can
673                achieve the same effect using the SimpleTest class generator.
674                In your library file, say <em>mocks/connection.php</em> for a
675                database connection, create a mock and inherit to override
676                special methods or add presets...
679    require_once('simpletest/mock_objects.php');
680    require_once('../classes/connection.php');
682    Mock::generate('Connection', 'BasicMockConnection');
683    class MockConnection extends BasicMockConnection {
684        function MockConnection() {
685            $this-&gt;BasicMockConnection();
686            $this-&gt;setReturn('query', false);
687        }
688    }</strong>
691                The generate call tells the class generator to create
692                a class called <span class="new_code">BasicMockConnection</span>
693                rather than the usual <span class="new_code">MockConnection</span>.
694                We then inherit from this to get our version of
695                <span class="new_code">MockConnection</span>.
696                By intercepting in this way we can add behaviour, here setting
697                the default value of <span class="new_code">query()</span> to be false.
698                By using the default name we make sure that the mock class
699                generator will not recreate a different one when invoked elsewhere in the
700                tests.
701                It never creates a class if it already exists.
702                As long as the above file is included first then all tests
703                that generated <span class="new_code">MockConnection</span> should
704                now be using our one instead.
705                If we don't get the order right and the mock library
706                creates one first then the class creation will simply fail.
707            </p>
708            <p>
709                Use this trick if you find you have a lot of common mock behaviour
710                or you are getting frequent integration problems at later
711                stages of testing.
712            </p>
714    </div>
715        References and related information...
716        <ul>
718            The original
719            <a href="">Mock objects</a> paper.
720        </li>
722            SimpleTest project page on <a href="">SourceForge</a>.
723        </li>
725            SimpleTest home page on <a href="">LastCraft</a>.
726        </li>
728<div class="menu_back"><div class="menu">
729<a href="index.html">SimpleTest</a>
730                |
731                <a href="overview.html">Overview</a>
732                |
733                <a href="unit_test_documentation.html">Unit tester</a>
734                |
735                <a href="group_test_documentation.html">Group tests</a>
736                |
737                <span class="chosen">Mock objects</span>
738                |
739                <a href="partial_mocks_documentation.html">Partial mocks</a>
740                |
741                <a href="reporter_documentation.html">Reporting</a>
742                |
743                <a href="expectation_documentation.html">Expectations</a>
744                |
745                <a href="web_tester_documentation.html">Web tester</a>
746                |
747                <a href="form_testing_documentation.html">Testing forms</a>
748                |
749                <a href="authentication_documentation.html">Authentication</a>
750                |
751                <a href="browser_documentation.html">Scriptable browser</a>
753<div class="copyright">
754            Copyright<br>Marcus Baker 2006
755        </div>
Note: See TracBrowser for help on using the repository browser.