Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<html>
2
<head>
3
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
4
<title>
5
        Extending the SimpleTest unit tester with additional expectation classes
6
    </title>
7
<link rel="stylesheet" type="text/css" href="docs.css" title="Styles">
8
</head>
9
<body>
10
<div class="menu_back">
11
<div class="menu">
12
<h2>
13
<a href="index.html">SimpleTest</a>
14
</h2>
15
<ul>
16
<li>
17
<a href="overview.html">Overview</a>
18
</li>
19
<li>
20
<a href="unit_test_documentation.html">Unit tester</a>
21
</li>
22
<li>
23
<a href="group_test_documentation.html">Group tests</a>
24
</li>
25
<li>
26
<a href="mock_objects_documentation.html">Mock objects</a>
27
</li>
28
<li>
29
<a href="partial_mocks_documentation.html">Partial mocks</a>
30
</li>
31
<li>
32
<a href="reporter_documentation.html">Reporting</a>
33
</li>
34
<li>
35
<span class="chosen">Expectations</span>
36
</li>
37
<li>
38
<a href="web_tester_documentation.html">Web tester</a>
39
</li>
40
<li>
41
<a href="form_testing_documentation.html">Testing forms</a>
42
</li>
43
<li>
44
<a href="authentication_documentation.html">Authentication</a>
45
</li>
46
<li>
47
<a href="browser_documentation.html">Scriptable browser</a>
48
</li>
49
</ul>
50
</div>
51
</div>
52
<h1>Expectation documentation</h1>
53
<div class="content">
54
        <p>
55
<a class="target" name="mock">
56
<h2>More control over mock objects</h2>
57
</a>
58
</p>
59
            <p>
60
                The default behaviour of the
61
                <a href="mock_objects_documentation.html">mock objects</a>
62
                in
63
                <a href="http://sourceforge.net/projects/simpletest/">SimpleTest</a>
64
                is either an identical match on the argument or to allow any argument at all.
65
                For almost all tests this is sufficient.
66
                Sometimes, though, you want to weaken a test case.
67
            </p>
68
            <p>
69
                One place where a test can be too tightly coupled is with
70
                text matching.
71
                Suppose we have a component that outputs a helpful error
72
                message when something goes wrong.
73
                You want to test that the correct error was sent, but the actual
74
                text may be rather long.
75
                If you test for the text exactly, then every time the exact wording
76
                of the message changes, you will have to go back and edit the test suite.
77
            </p>
78
            <p>
79
                For example, suppose we have a news service that has failed
80
                to connect to its remote source.
81
<pre>
82
<strong>class NewsService {
83
    ...
84
    function publish(&amp;$writer) {
85
        if (! $this-&gt;isConnected()) {
86
            $writer-&gt;write('Cannot connect to news service "' .
87
                    $this-&gt;_name . '" at this time. ' .
88
                    'Please try again later.');
89
        }
90
        ...
91
    }
92
}</strong>
93
</pre>
94
                Here it is sending its content to a
95
                <span class="new_code">Writer</span> class.
96
                We could test this behaviour with a
97
                <span class="new_code">MockWriter</span> like so...
98
<pre>
99
class TestOfNewsService extends UnitTestCase {
100
    ...
101
    function testConnectionFailure() {&lt;strong&gt;
102
        $writer = &amp;new MockWriter();
103
        $writer-&gt;expectOnce('write', array(
104
                'Cannot connect to news service ' .
105
                '"BBC News" at this time. ' .
106
                'Please try again later.'));
107
 
108
        $service = &amp;new NewsService('BBC News');
109
        $service-&gt;publish($writer);
110
    }
111
}
112
</pre>
113
                This is a good example of a brittle test.
114
                If we decide to add additional instructions, such as
115
                suggesting an alternative news source, we will break
116
                our tests even though no underlying functionality
117
                has been altered.
118
            </p>
119
            <p>
120
                To get around this, we would like to do a regular expression
121
                test rather than an exact match.
122
                We can actually do this with...
123
<pre>
124
class TestOfNewsService extends UnitTestCase {
125
    ...
126
    function testConnectionFailure() {
127
        $writer = &amp;new MockWriter();<strong>
128
        $writer-&gt;expectOnce(
129
                'write',
130
                array(new PatternExpectation('/cannot connect/i')));</strong>
131
 
132
        $service = &amp;new NewsService('BBC News');
133
        $service-&gt;publish($writer);
134
    }
135
}
136
</pre>
137
                Instead of passing in the expected parameter to the
138
                <span class="new_code">MockWriter</span> we pass an
139
                expectation class called
140
                <span class="new_code">WantedPatternExpectation</span>.
141
                The mock object is smart enough to recognise this as special
142
                and to treat it differently.
143
                Rather than simply comparing the incoming argument to this
144
                object, it uses the expectation object itself to
145
                perform the test.
146
            </p>
147
            <p>
148
                The <span class="new_code">WantedPatternExpectation</span> takes
149
                the regular expression to match in its constructor.
150
                Whenever a comparison is made by the <span class="new_code">MockWriter</span>
151
                against this expectation class, it will do a
152
                <span class="new_code">preg_match()</span> with this pattern.
153
                With our test case above, as long as "cannot connect"
154
                appears in the text of the string, the mock will issue a pass
155
                to the unit tester.
156
                The rest of the text does not matter.
157
            </p>
158
            <p>
159
                The possible expectation classes are...
160
                <table>
161
<tbody>
162
                    <tr>
163
<td><span class="new_code">EqualExpectation</span></td><td>An equality, rather than the stronger identity comparison</td>
164
</tr>
165
                    <tr>
166
<td><span class="new_code">NotEqualExpectation</span></td><td>An inequality comparison</td>
167
</tr>
168
                    <tr>
169
<td><span class="new_code">IndenticalExpectation</span></td><td>The default mock object check which must match exactly</td>
170
</tr>
171
                    <tr>
172
<td><span class="new_code">NotIndenticalExpectation</span></td><td>Inverts the mock object logic</td>
173
</tr>
174
                    <tr>
175
<td><span class="new_code">PatternExpectation</span></td><td>Uses a Perl Regex to match a string</td>
176
</tr>
177
                    <tr>
178
<td><span class="new_code">NoPatternExpectation</span></td><td>Passes only if failing a Perl Regex</td>
179
</tr>
180
                    <tr>
181
<td><span class="new_code">IsAExpectation</span></td><td>Checks the type or class name only</td>
182
</tr>
183
                    <tr>
184
<td><span class="new_code">NotAExpectation</span></td><td>Opposite of the <span class="new_code">IsAExpectation</span></td>
185
</tr>
186
                    <tr>
187
<td><span class="new_code">MethodExistsExpectation</span></td><td>Checks a method is available on an object</td>
188
</tr>
189
                </tbody>
190
</table>
191
                Most take the expected value in the constructor.
192
                The exceptions are the pattern matchers, which take a regular expression,
193
                and the <span class="new_code">IsAExpectation</span> and <span class="new_code">NotAExpectation</span> which takes a type
194
                or class name as a string.
195
            </p>
196
 
197
        <p>
198
<a class="target" name="behaviour">
199
<h2>Using expectations to control stubs</h2>
200
</a>
201
</p>
202
            <p>
203
                The expectation classes can be used not just for sending assertions
204
                from mock objects, but also for selecting behaviour for the
205
                <a href="mock_objects_documentation.html">mock objects</a>.
206
                Anywhere a list of arguments is given, a list of expectation objects
207
                can be inserted instead.
208
            </p>
209
            <p>
210
                Suppose we want an authorisation server mock to simulate a successful login
211
                only if it receives a valid session object.
212
                We can do this as follows...
213
<pre>
214
Mock::generate('Authorisation');
215
<strong>
216
$authorisation = new MockAuthorisation();
217
$authorisation-&gt;setReturnValue(
218
        'isAllowed',
219
        true,
220
        array(new IsAExpectation('Session', 'Must be a session')));
221
$authorisation-&gt;setReturnValue('isAllowed', false);</strong>
222
</pre>
223
                We have set the default mock behaviour to return false when
224
                <span class="new_code">isAllowed</span> is called.
225
                When we call the method with a single parameter that
226
                is a <span class="new_code">Session</span> object, it will return true.
227
                We have also added a second parameter as a message.
228
                This will be displayed as part of the mock object
229
                failure message if this expectation is the cause of
230
                a failure.
231
            </p>
232
            <p>
233
                This kind of sophistication is rarely useful, but is included for
234
                completeness.
235
            </p>
236
 
237
        <p>
238
<a class="target" name="extending">
239
<h2>Creating your own expectations</h2>
240
</a>
241
</p>
242
            <p>
243
                The expectation classes have a very simple structure.
244
                So simple that it is easy to create your own versions for
245
                commonly used test logic.
246
            </p>
247
            <p>
248
                As an example here is the creation of a class to test for
249
                valid IP addresses.
250
                In order to work correctly with the stubs and mocks the new
251
                expectation class should extend
252
                <span class="new_code">SimpleExpectation</span>...
253
<pre>
254
<strong>class ValidIp extends SimpleExpectation {
255
 
256
    function test($ip) {
257
        return (ip2long($ip) != -1);
258
    }
259
 
260
    function testMessage($ip) {
261
        return "Address [$ip] should be a valid IP address";
262
    }
263
}</strong>
264
</pre>
265
                There are only two methods to implement.
266
                The <span class="new_code">test()</span> method should
267
                evaluate to true if the expectation is to pass, and
268
                false otherwise.
269
                The <span class="new_code">testMessage()</span> method
270
                should simply return some helpful text explaining the test
271
                that was carried out.
272
            </p>
273
            <p>
274
                This class can now be used in place of the earlier expectation
275
                classes.
276
            </p>
277
 
278
        <p>
279
<a class="target" name="unit">
280
<h2>Under the bonnet of the unit tester</h2>
281
</a>
282
</p>
283
            <p>
284
                The <a href="http://sourceforge.net/projects/simpletest/">SimpleTest unit testing framework</a>
285
                also uses the expectation classes internally for the
286
                <a href="unit_test_documentation.html">UnitTestCase class</a>.
287
                We can also take advantage of these mechanisms to reuse our
288
                homebrew expectation classes within the test suites directly.
289
            </p>
290
            <p>
291
                The most crude way of doing this is to use the
292
                <span class="new_code">SimpleTest::assert()</span> method to
293
                test against it directly...
294
<pre>
295
<strong>class TestOfNetworking extends UnitTestCase {
296
    ...
297
    function testGetValidIp() {
298
        $server = &amp;new Server();
299
        $this-&gt;assert(
300
                new ValidIp(),
301
                $server-&gt;getIp(),
302
                'Server IP address-&gt;%s');
303
    }
304
}</strong>
305
</pre>
306
                This is a little untidy compared with our usual
307
                <span class="new_code">assert...()</span> syntax.
308
            </p>
309
            <p>
310
                For such a simple case we would normally create a
311
                separate assertion method on our test case rather
312
                than bother using the expectation class.
313
                If we pretend that our expectation is a little more
314
                complicated for a moment, so that we want to reuse it,
315
                we get...
316
<pre>
317
class TestOfNetworking extends UnitTestCase {
318
    ...<strong>
319
    function assertValidIp($ip, $message = '%s') {
320
        $this-&gt;assert(new ValidIp(), $ip, $message);
321
    }</strong>
322
 
323
    function testGetValidIp() {
324
        $server = &amp;new Server();<strong>
325
        $this-&gt;assertValidIp(
326
                $server-&gt;getIp(),
327
                'Server IP address-&gt;%s');</strong>
328
    }
329
}
330
</pre>
331
                It is unlikely we would ever need this degree of control
332
                over the testing machinery.
333
                It is rare to need the expectations for more than pattern
334
                matching.
335
                Also, complex expectation classes could make the tests
336
                harder to read and debug.
337
                These mechanisms are really of most use to authors of systems
338
                that will extend the test framework to create their own tool set.
339
            </p>
340
 
341
    </div>
342
<div class="copyright">
343
            Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004
344
        </div>
345
</body>
346
</html>