File: mock_objects_tutorial.xml

package info (click to toggle)
postfixadmin 2.3.5-2%2Bdeb7u1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 6,200 kB
  • sloc: php: 25,767; xml: 14,485; perl: 964; sh: 664; python: 169; makefile: 84
file content (361 lines) | stat: -rw-r--r-- 14,258 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
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
<?xml version="1.0"?>
<page title="Mock Objects" here="Using mock objects">
    <long_title>PHP unit testing tutorial - Using mock objects in PHP</long_title>
    <content>
        <p>
            <a class="target" name="refactor"><h2>Refactoring the tests again</h2></a>
        </p>
        <p>
            Before more functionality is added there is some refactoring
            to do.
            We are going to do some timing tests and so the
            <code>TimeTestCase</code> class definitely needs
            its own file.
            Let&apos;s say <em>tests/time_test_case.php</em>...
<php><![CDATA[
<strong><?php
    if (! defined('SIMPLE_TEST')) {
        define('SIMPLE_TEST', 'simpletest/');
    }
    require_once(SIMPLE_TEST . 'unit_tester.php');

    class TimeTestCase extends UnitTestCase {
        function TimeTestCase($test_name = '') {
            $this->UnitTestCase($test_name);
        }
        function assertSameTime($time1, $time2, $message = '') {
            if (! $message) {
                $message = "Time [$time1] should match time [$time2]";
            }
            $this->assertTrue(
                    ($time1 == $time2) || ($time1 + 1 == $time2),
                    $message);
        }
    }
?></strong>
]]></php>
            We can then <code>require()</code> this file into
            the <em>all_tests.php</em> script.
        </p>
        <p>
            <a class="target" name="timestamp"><h2>Adding a timestamp to the Log</h2></a>
        </p>
        <p>
            I don&apos;t know quite what the format of the log message should
            be for the test, so to check for a timestamp we could do the
            simplest possible thing, which is to look for a sequence of digits.
<php><![CDATA[
<?php
    require_once('../classes/log.php');<strong>
    require_once('../classes/clock.php');

    class TestOfLogging extends TimeTestCase {
        function TestOfLogging() {
            $this->TimeTestCase('Log class test');
        }</strong>
        function setUp() {
            @unlink('../temp/test.log');
        }
        function tearDown() {
            @unlink('../temp/test.log');
        }
        function getFileLine($filename, $index) {
            $messages = file($filename);
            return $messages[$index];
        }
        function testCreatingNewFile() {
            ...
        }
        function testAppendingToFile() {
            ...
        }<strong>
        function testTimestamps() {
            $log = new Log('../temp/test.log');
            $log->message('Test line');
            $this->assertTrue(
                    preg_match('/(\d+)/', $this->getFileLine('../temp/test.log', 0), $matches),
                    'Found timestamp');
            $clock = new clock();
            $this->assertSameTime((integer)$matches[1], $clock->now(), 'Correct time');
        }</strong>
    }
?>
]]></php>
            The test case creates a new <code>Log</code>
            object and writes a message.
            We look for a digit sequence and then test it against the current
            time using our <code>Clock</code> object.
            Of course it doesn&apos;t work until we write the code.
            <div class="demo">
                <h1>All tests</h1>
                <span class="pass">Pass</span>: log_test.php->Log class test->testappendingtofile->Expecting [/Test line 1/] in [Test line 1]<br />
                <span class="pass">Pass</span>: log_test.php->Log class test->testappendingtofile->Expecting [/Test line 2/] in [Test line 2]<br />
                <span class="pass">Pass</span>: log_test.php->Log class test->testcreatingnewfile->Created before message<br />
                <span class="pass">Pass</span>: log_test.php->Log class test->testcreatingnewfile->File created<br />
                <span class="fail">Fail</span>: log_test.php->Log class test->testtimestamps->Found timestamp<br />
                <br />
                <b>Notice</b>:  Undefined offset:  1 in <b>/home/marcus/projects/lastcraft/tutorial_tests/tests/log_test.php</b> on line <b>44</b><br />
                <span class="fail">Fail</span>: log_test.php->Log class test->testtimestamps->Correct time<br />
                <span class="pass">Pass</span>: clock_test.php->Clock class test->testclockadvance->Advancement<br />
                <span class="pass">Pass</span>: clock_test.php->Clock class test->testclocktellstime->Now is the right time<br />
                <div style="padding: 8px; margin-top: 1em; background-color: red; color: white;">3/3 test cases complete.
                <strong>6</strong> passes and <strong>2</strong> fails.</div>
            </div>
            The test suite is still showing the passes from our earlier
            modification.
        </p>
        <p>
            We can get the tests to pass simply by adding a timestamp
            when writing out to the file.
            Yes, of course all of this is trivial and
            I would not normally test this fanatically, but it is going
            to illustrate a more general problem.
            The <em>log.php</em> file becomes...
<php><![CDATA[
<?php<strong>
    require_once('../classes/clock.php');</strong>
    
    class Log {
        var $_file_path;
        
        function Log($file_path) {
            $this->_file_path = $file_path;
        }
        
        function message($message) {<strong>
            $clock = new Clock();</strong>
            $file = fopen($this->_file_path, 'a');<strong>
            fwrite($file, "[" . $clock->now() . "] $message\n");</strong>
            fclose($file);
        }
    }
?>
]]></php>
            The tests should now pass.
        </p>
        <p>
            Our new test is full of problems, though.
            What if our time format changes to something else?
            Things are going to be a lot more complicated to test if this
            happens.
            It also means that any changes to the clock class time
            format will cause our logging tests to fail also.
            This means that our log tests are tangled up with the clock tests
            and extremely fragile.
            It lacks cohesion, which is the same as saying it is not
            tightly focused, testing facets of the clock as well as the log.
            Our problems are caused in part because the clock output
            is unpredictable when
            all we really want to test is that the logging message
            contains the output of
            <code>Clock::now()</code>.
            We don&apos;t
            really care about the contents of that method call.
        </p>
        <p>
            Can we make that call predictable?
            We could if we could get the log to use a dummy version
            of the clock for the duration of the test.
            The dummy clock class would have to behave the same way
            as the <code>Clock</code> class
            except for the fixed output from the
            <code>now()</code> method.
            Hey, that would even free us from using the
            <code>TimeTestCase</code> class!
        </p>
        <p>
            We could write such a class pretty easily although it is
            rather tedious work.
            We just create another clock class with same interface
            except that the <code>now()</code> method
            returns a value that we can change with some other setter method.
            That is quite a lot of work for a pretty minor test.
        </p>
        <p>
            Except that it is really no work at all.
        </p>
        <p>
            <a class="target" name="mock"><h2>A mock clock</h2></a>
        </p>
        <p>
            To reach instant testing clock nirvana we need
            only three extra lines of code...
<php><![CDATA[
require_once('simpletest/mock_objects.php');
]]></php>
            This includes the mock generator code.
            It is simplest to place this in the <em>all_tests.php</em>
            script as it gets used rather a lot.
<php><![CDATA[
Mock::generate('Clock');
]]></php>
            This is the line that does the work.
            The code generator scans the class for all of its
            methods, creates code to generate an identically
            interfaced class, but with the name &quot;Mock&quot; added,
            and then <code>eval()</code>s the new code to
            create the new class.
<php><![CDATA[
$clock = &new MockClock($this);
]]></php>
            This line can be added to any test method we are interested in.
            It creates the dummy clock ready to receive our instructions.
        </p>
        <p>
            Our test case is on the first steps of a radical clean up...
<php><![CDATA[
<?php
    require_once('../classes/log.php');
    require_once('../classes/clock.php');<strong>
    Mock::generate('Clock');

    class TestOfLogging extends UnitTestCase {
        function TestOfLogging() {
            $this->UnitTestCase('Log class test');
        }</strong>
        function setUp() {
            @unlink('../temp/test.log');
        }
        function tearDown() {
            @unlink('../temp/test.log');
        }
        function getFileLine($filename, $index) {
            $messages = file($filename);
            return $messages[$index];
        }
        function testCreatingNewFile() {
            ...
        }
        function testAppendingToFile() {
            ...
        }
        function testTimestamps() {<strong>
            $clock = &new MockClock($this);
            $clock->setReturnValue('now', 'Timestamp');
            $log = new Log('../temp/test.log');
            $log->message('Test line', &$clock);
            $this->assertWantedPattern(
                    '/Timestamp/',
                    $this->getFileLine('../temp/test.log', 0),
                    'Found timestamp');</strong>
        }
    }
?>
]]></php>
            This test method creates a <code>MockClock</code>
            object and then sets the return value of the
            <code>now()</code> method to be the string
            &quot;Timestamp&quot;.
            Every time we call <code>$clock->now()</code>
            it will return this string.
            This should be easy to spot.
        </p>
        <p>
            Next we create our log and send a message.
            We pass into the <code>message()</code>
            call the clock we would like to use.
            This means that we will have to add an optional parameter to
            the logging class to make testing possible...
<php><![CDATA[
class Log {
    var $_file_path;
    
    function Log($file_path) {
        $this->_file_path = $file_path;
    }
    
    function message($message, <strong>$clock = false</strong>) {<strong>
        if (!is_object($clock)) {
            $clock = new Clock();
        }</strong>
        $file = fopen($this->_file_path, 'a');
        fwrite($file, "[" . $clock->now() . "] $message\n");
        fclose($file);
    }
}
]]></php>
            All of the tests now pass and they test only the logging code.
            We can breathe easy again.
        </p>
        <p>
            Does that extra parameter in the <code>Log</code>
            class bother you?
            We have changed the interface just to facilitate testing after
            all.
            Are not interfaces the most important thing?
            Have we sullied our class with test code?
        </p>
        <p>
            Possibly, but consider this.
            Next chance you get, look at a circuit board, perhaps the motherboard
            of the computer you are looking at right now.
            On most boards you will find the odd empty hole, or solder
            joint with nothing attached or perhaps a pin or socket
            that has no obvious function.
            Chances are that some of these are for expansion and
            variations, but most of the remainder will be for testing.
        </p>
        <p>
            Think about that.
            The factories making the boards many times over wasting material
            on parts that do not add to the final function.
            If hardware engineers can make this sacrifice of elegance I am
            sure we can too.
            Our sacrifice wastes no materials after all.
        </p>
        <p>
            Still bother you?
            Actually it bothers me too, but not so much here.
            The number one priority is code that works, not prizes
            for minimalism.
            If it really bothers you, then move the creation of the clock
            into another protected factory method.
            Then subclass the clock for testing and override the
            factory method with one that returns the mock.
            Your tests are clumsier, but your interface is intact.
        </p>
        <p>
            Again I leave the decision to you.
        </p>
    </content>
    <internal>
        <link>
            <a href="#refactor">Refactoring the tests</a> so we can reuse
            our new time test.
        </link>
        <link>Adding <a href="#timestamp">Log timestamps</a>.</link>
        <link><a href="#mock">Mocking the clock</a> to make the test cohesive.</link>
    </internal>
    <external>
        <link>
            This follows the <a href="first_test_tutorial.php">unit test tutorial</a>.
        </link>
        <link>
            Next is distilling <a href="boundary_classes_tutorial.php">boundary classes</a>.
        </link>
        <link>
            You will need the <a href="simple_test.php">SimpleTest</a>
            tool to run the examples.
        </link>
        <link>
            <a href="http://www.mockobjects.com/">Mock objects</a> papers.
        </link>
    </external>
    <meta>
        <keywords>
            software development,
            php programming,
            programming php,
            software development tools,
            php tutorial,
            free php scripts,
            architecture,
            php resources,
            mock objects,
            junit,
            php testing,
            unit test,
            php testing
        </keywords>
    </meta>
</page>