File: gain_control_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 (299 lines) | stat: -rw-r--r-- 11,538 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
<?xml version="1.0"?>
<page title="Taking control of testing" here="Taking control">
    <long_title>PHP unit testing tutorial - Isolating variables when testing</long_title>
    <content>
        <p>
            In order to test a code module you need very tight control
            of its environment.
            If anything can vary behind the scenes, for example a
            configuration file, then this could cause the tests
            to fail unexpectedly.
            This would not be a fair test of the code and could
            cause you to spend fruitless hours examining code that
            is actually working, rather than dealing with the configuration issue
            that actually failed the test.
            At the very least your test cases get more complicated in
            taking account the possible variations.
        </p>
        <p>
            <a class="target" name="time"><h2>Controlling time</h2></a>
        <p>
        </p>
            There are often a lot of obvious variables that could affect
            a unit test case, especially in the web development
            environment in which PHP usually operates.
            These include database set up, file permissions, network
            resources and configuration amongst others.
            The failure or misinstall of one of these components will
            break the test suite.
            Do we add tests to confirm these components are installed?
            This is a good idea, but if you place them into code module
            tests you will start to clutter you test code with detail
            that is irrelavent to the immediate task.
            They should be placed in their own test group.
        </p>
        <p>
            Another problem, though, is that our development machines
            must have every system component installed to be able
            to run the test suite.
            Your tests run slower too.
        </p>
        <p>
            When faced with this while coding we will often create wrapper
            versions of classes that deal with these resources.
            Ugly details of these resources are then coded once only.
            I like to call these classes &quot;boundary classes&quot;
            as they exist at the edges of the application, 
            the interface of your application with the rest of the
            system.
            These boundary classes are best simulated during testing
            by simulated versions.
            These run faster as well and are often called
            &quot;Server Stubs&quot; or in more generic form
            &quot;Mock Objects&quot;.
            It is a great time saver to wrap and stub out every such resource.
        </p>
        <p>
            One often neglected factor is time.
            For example, to test a session time-out coders will often
            temporarily set the session time limit to a small value, say two seconds,
            and then do a <code>sleep(3)</code>
            and assert that the session is now invalid.
            That adds three seconds to your test suite and is usually
            a lot of extra code making your session classes that maleable.
            Far simpler is to have a way to suddenly advance the clock.
            To control time.
        </p>
        <p>
            <a class="target" name="clock"><h2>A clock class</h2></a>
            Again we will design our clock wrapper by writing tests.
            Firstly we add a clock test case to our <em>tests/all_tests.php</em>
            test suite...
<php><![CDATA[
<?php
    if (! defined('SIMPLE_TEST')) {
        define('SIMPLE_TEST', 'simpletest/');
    }
    require_once(SIMPLE_TEST . 'unit_tester.php');
    require_once(SIMPLE_TEST . 'reporter.php');
    require_once('log_test.php');<strong>
    require_once('clock_test.php');</strong>

    $test = &new TestSuite('All tests');
    $test->addTestCase(new TestOfLogging());<strong>
    $test->addTestCase(new TestOfClock());</strong>
    $test->run(new HtmlReporter());
?>
]]></php>
            Then we create the test case in the new file
            <em>tests/clock_test.php</em>...
<php><![CDATA[
<strong><?php
    require_once('../classes/clock.php');

    class TestOfClock extends UnitTestCase {
        function TestOfClock() {
            $this->UnitTestCase('Clock class test');
        }
        function testClockTellsTime() {
            $clock = new Clock();
            $this->assertEqual($clock->now(), time(), 'Now is the right time');
        }
        function testClockAdvance() {
        }
    }
?></strong>
]]></php>
            Our only test at the moment is that our new
            <code>Clock</code> class acts
            as a simple PHP <code>time()</code>
            function substitute.
            The other method is a place holder.
            It&apos;s our <em>TODO</em> item if you like.
            We haven&apos;t done a test for it yet because that
            would spoil our rhythm.
            We will write the time shift functionality once we are green.
            At the moment we are obviously not green...
            <div class="demo">
                <br />
                <b>Fatal error</b>:  Failed opening required '../classes/clock.php' (include_path='') in
                <b>/home/marcus/projects/lastcraft/tutorial_tests/tests/clock_test.php</b> on line <b>2</b>
                <br />
            </div>
            We create a <em>classes/clock.php</em> file like so...
<php><![CDATA[
<strong><?php
    class Clock {
        
        function Clock() {
        }
        
        function now() {
        }
    }
?></strong>
]]></php>
            This regains our flow ready for coding.
            <div class="demo">
                <h1>All tests</h1>
                <span class="fail">Fail</span>: Clock class test-&gt;testclocktellstime-&gt;[NULL: ] should be equal to [integer: 1050257362]<br />
                <div style="padding: 8px; margin-top: 1em; background-color: red; color: white;">3/3 test cases complete.
                <strong>4</strong> passes and <strong>1</strong> fails.</div>
            </div>
            This is now easy to fix...
<php><![CDATA[
class Clock {
    
    function Clock() {
    }
    
    function now() {<strong>
        return time();</strong>
    }
}
]]></php>
            And now we are green...
            <div class="demo">
                <h1>All tests</h1>
                <div style="padding: 8px; margin-top: 1em; background-color: green; color: white;">3/3 test cases complete.
                <strong>5</strong> passes and <strong>0</strong> fails.</div>
            </div>
            There is still a problem.
            The clock could roll over during the assertion causing the
            result to be out by one second.
            The chances are small, but if there were a lot of timing tests
            you would end up with a test suite that is erratic, severely
            limiting its usefulness.
            We will <a href="subclass_tutorial.php">tackle this shortly</a> and for now just jot it onto our
            &quot;to do&quot; list.
        </p>
        <p>
            The advancement test looks like this...
<php><![CDATA[
class TestOfClock extends UnitTestCase {
    function TestOfClock() {
        $this->UnitTestCase('Clock class test');
    }
    function testClockTellsTime() {
        $clock = new Clock();
        $this->assertEqual($clock->now(), time(), 'Now is the right time');
    }<strong>
    function testClockAdvance() {
        $clock = new Clock();
        $clock->advance(10);
        $this->assertEqual($clock->now(), time() + 10, 'Advancement');
    }</strong>
}
]]></php>
            The code to get to green is straight forward and just involves adding
            a time offset.
<php><![CDATA[
class Clock {<strong>
    var $_offset;</strong>
    
    function Clock() {<strong>
        $this->_offset = 0;</strong>
    }
    
    function now() {
        return time()<strong> + $this->_offset</strong>;
    }
    <strong>
    function advance($offset) {
        $this->_offset += $offset;
    }</strong>
}
]]></php>
        </p>
        <p>
            <a class="target" name="tidy"><h2>Group test tidy up</h2></a>
            Our <em>all_tests.php</em> file has some repetition we could
            do without.
            We have to manually add our test cases from each included
            file.
            It is possible to remove it, but use of the following requires care.
            The <code>TestSuite</code> class has a
            convenience method called <code>addTestFile()</code>
            that takes a PHP file as a parameter.
            This mechanism makes a note of all the classes, requires in the
            file and then has a look at any newly created classes.
            If they are descendents of <code>TestCase</code>
            they are added as a new group test.
        </p>
        <p>
            Here is our refactored test suite using this method...
<php><![CDATA[
<?php
    if (! defined('SIMPLE_TEST')) {
        define('SIMPLE_TEST', 'simpletest/');
    }<strong>
    require_once(SIMPLE_TEST . 'unit_tester.php');
    require_once(SIMPLE_TEST . 'reporter.php');</strong>

    $test = &new TestSuite('All tests');<strong>
    $test->addTestFile('log_test.php');
    $test->addTestFile('clock_test.php');</strong>
    $test->run(new HtmlReporter());
?>
]]></php>
            The pitfalls of this are...
            <ol>
                <li>
                    If the test file has already been included,
                    no new classes will be added to this group
                </li>
                <li>
                    If the test file has other classes that are
                    related to <code>TestCase</code>
                    then these will be added to the group test as well.
                </li>
            </ol>
            In our tests we have only test cases in the test files and
            we removed their inclusion from the <em>all_tests.php</em>
            script and so we are OK.
            This is the usual situation.
        </p>
        <p>
            We should really fix the glitch with the possible
            clock rollover so we&apos;ll
            <a href="subclass_tutorial.php">do this next</a>.
        </p>
    </content>
    <internal>
        <link><a href="#time">Time</a> is an often neglected variable in tests.</link>
        <link>A <a href="#clock">clock class</a> allows us to alter time.</link>
        <link><a href="#tidy">Tidying the group test</a>.</link>
    </internal>
    <external>
        <link>
            The previous section is
            <a href="group_test_tutorial.php">grouping unit tests</a>.
        </link>
        <link>
            The next section is
            <a href="subclass_tutorial.php">subclassing test cases</a>.
        </link>
        <link>
            You will need
            <a href="simple_test.php">the SimpleTest unit tester</a>
            for the examples.
        </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>