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
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
|
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Documentation SimpleTest : étendre le testeur unitaire avec des classes d'attentes supplémentaires</title>
<link rel="stylesheet" type="text/css" href="docs.css" title="Styles">
</head>
<body>
<div class="menu_back"><div class="menu">
<a href="index.html">SimpleTest</a>
|
<a href="overview.html">Overview</a>
|
<a href="unit_test_documentation.html">Unit tester</a>
|
<a href="group_test_documentation.html">Group tests</a>
|
<a href="mock_objects_documentation.html">Mock objects</a>
|
<a href="partial_mocks_documentation.html">Partial mocks</a>
|
<a href="reporter_documentation.html">Reporting</a>
|
<a href="expectation_documentation.html">Expectations</a>
|
<a href="web_tester_documentation.html">Web tester</a>
|
<a href="form_testing_documentation.html">Testing forms</a>
|
<a href="authentication_documentation.html">Authentication</a>
|
<a href="browser_documentation.html">Scriptable browser</a>
</div></div>
<h1>Documentation sur les attentes</h1>
This page...
<ul>
<li>
Utiliser les attentes <a href="#fantaisie">pour des tests
plus précis avec des objets fantaisie</a>
</li>
<li>
<a href="#comportement">Changer le comportement d'un objet fantaisie</a>
avec des attentes
</li>
<li>
<a href="#etendre">Créer des attentes</a>
</li>
<li>
Par dessous SimpleTest <a href="#unitaire">utilise des classes d'attente</a>
</li>
</ul>
<div class="content">
<p><a class="target" name="fantaisie"><h2>Plus de contrôle sur les objets fantaisie</h2></a></p>
<p>
Le comportement par défaut des
<a href="mock_objects_documentation.html">objets fantaisie</a> dans
<a href="http://sourceforge.net/projects/simpletest/">SimpleTest</a>
est soit une correspondance identique sur l'argument,
soit l'acceptation de n'importe quel argument.
Pour la plupart des tests, c'est suffisant.
Cependant il est parfois nécessaire de ramollir un scénario de test.
</p>
<p>
Un des endroits où un test peut être trop serré
est la reconnaissance textuelle. Prenons l'exemple
d'un composant qui produirait un message d'erreur
utile lorsque quelque chose plante. Il serait utile de tester
que l'erreur correcte est renvoyée,
mais le texte proprement dit risque d'être plutôt long.
Si vous testez le texte dans son ensemble alors
à chaque modification de ce même message
-- même un point ou une virgule -- vous aurez
à revenir sur la suite de test pour la modifier.
</p>
<p>
Voici un cas concret, nous avons un service d'actualités
qui a échoué dans sa tentative de connexion à sa source distante.
<pre>
<strong>class NewsService {
...
function publish(&$writer) {
if (! $this->isConnected()) {
$writer->write('Cannot connect to news service "' .
$this->_name . '" at this time. ' .
'Please try again later.');
}
...
}
}</strong>
</pre>
Là il envoie son contenu vers un classe <span class="new_code">Writer</span>.
Nous pourrions tester ce comportement avec un <span class="new_code">MockWriter</span>...
<pre>
class TestOfNewsService extends UnitTestCase {
...
function testConnectionFailure() {<strong>
$writer = &new MockWriter($this);
$writer->expectOnce('write', array(
'Cannot connect to news service ' .
'"BBC News" at this time. ' .
'Please try again later.'));
$service = &new NewsService('BBC News');
$service->publish($writer);
$writer->tally();</strong>
}
}
</pre>
C'est un bon exemple d'un test fragile.
Si nous décidons d'ajouter des instructions complémentaires,
par exemple proposer une source d'actualités alternative,
nous casserons nos tests par la même occasion sans pourtant
avoir modifié une seule fonctionnalité.
</p>
<p>
Pour contourner ce problème, nous voudrions utiliser
un test avec une expression rationnelle plutôt
qu'une correspondance exacte. Nous pouvons y parvenir avec...
<pre>
class TestOfNewsService extends UnitTestCase {
...
function testConnectionFailure() {
$writer = &new MockWriter($this);<strong>
$writer->expectOnce(
'write',
array(new WantedPatternExpectation('/cannot connect/i')));</strong>
$service = &new NewsService('BBC News');
$service->publish($writer);
$writer->tally();
}
}
</pre>
Plutôt que de transmettre le paramètre attendu au <span class="new_code">MockWriter</span>,
nous envoyons une classe d'attente appelée <span class="new_code">WantedPatternExpectation</span>.
L'objet fantaisie est suffisamment élégant pour reconnaître
qu'il s'agit d'un truc spécial et pour le traiter différemment.
Plutôt que de comparer l'argument entrant à cet objet,
il utilise l'objet attente lui-même pour exécuter le test.
</p>
<p>
<span class="new_code">WantedPatternExpectation</span> utilise
l'expression rationnelle pour la comparaison avec son constructeur.
A chaque fois qu'une comparaison est fait à travers
<span class="new_code">MockWriter</span> par rapport à cette classe attente,
elle fera un <span class="new_code">preg_match()</span> avec ce motif.
Dans notre scénario de test ci-dessus, aussi longtemps
que la chaîne "cannot connect" apparaît dans le texte,
la fantaisie transmettra un succès au testeur unitaire.
Peu importe le reste du texte.
</p>
<p>
Les classes attente possibles sont...
<table><tbody>
<tr>
<td><span class="new_code">EqualExpectation</span></td>
<td>Une égalité, plutôt que la plus forte comparaison à l'identique</td>
</tr>
<tr>
<td><span class="new_code">NotEqualExpectation</span></td>
<td>Une comparaison sur la non-égalité</td>
</tr>
<tr>
<td><span class="new_code">IndenticalExpectation</span></td>
<td>La vérification par défaut de l'objet fantaisie qui doit correspondre exactement</td>
</tr>
<tr>
<td><span class="new_code">NotIndenticalExpectation</span></td>
<td>Inverse la logique de l'objet fantaisie</td>
</tr>
<tr>
<td><span class="new_code">WantedPatternExpectation</span></td>
<td>Utilise une expression rationnelle Perl pour comparer une chaîne</td>
</tr>
<tr>
<td><span class="new_code">NoUnwantedPatternExpectation</span></td>
<td>Passe seulement si l'expression rationnelle Perl échoue</td>
</tr>
<tr>
<td><span class="new_code">IsAExpectation</span></td>
<td>Vérifie le type ou le nom de la classe uniquement</td>
</tr>
<tr>
<td><span class="new_code">NotAExpectation</span></td>
<td>L'opposé de <span class="new_code">IsAExpectation</span>
</td>
</tr>
<tr>
<td><span class="new_code">MethodExistsExpectation</span></td>
<td>Vérifie si la méthode est disponible sur un objet</td>
</tr>
</tbody></table>
La plupart utilisent la valeur attendue dans le constructeur.
Les exceptions sont les vérifications sur motif,
qui utilisent une expression rationnelle, ainsi que
<span class="new_code">IsAExpectation</span> et <span class="new_code">NotAExpectation</span>,
qui prennent un type ou un nom de classe comme chaîne.
</p>
<p><a class="target" name="comportement"><h2>Utiliser les attentes pour contrôler les bouchons serveur</h2></a></p>
<p>
Les classes attente peuvent servir à autre chose
que l'envoi d'assertions depuis les objets fantaisie,
afin de choisir le comportement d'un
<a href="mock_objects_documentation.html">objet fantaisie</a>
ou celui d'un <a href="server_stubs_documentation.html">bouchon serveur</a>.
A chaque fois qu'une liste d'arguments est donnée,
une liste d'objets d'attente peut être insérée à la place.
</p>
<p>
Mettons que nous voulons qu'un bouchon serveur
d'autorisation simule une connexion réussie seulement
si il reçoit un objet de session valide.
Nous pouvons y arriver avec ce qui suit...
<pre>
Stub::generate('Authorisation');
<strong>
$authorisation = new StubAuthorisation();
$authorisation->setReturnValue(
'isAllowed',
true,
array(new IsAExpectation('Session', 'Must be a session')));
$authorisation->setReturnValue('isAllowed', false);</strong>
</pre>
Le comportement par défaut du bouchon serveur
est défini pour renvoyer <span class="new_code">false</span>
quand <span class="new_code">isAllowed</span> est appelé.
Lorsque nous appelons cette méthode avec un unique paramètre
qui est un objet <span class="new_code">Session</span>, il renverra <span class="new_code">true</span>.
Nous avons aussi ajouté un deuxième paramètre comme message.
Il sera affiché dans le message d'erreur de l'objet fantaisie
si l'attente est la cause de l'échec.
</p>
<p>
Ce niveau de sophistication est rarement utile :
il n'est inclut que pour être complet.
</p>
<p><a class="target" name="etendre"><h2>Créer vos propres attentes</h2></a></p>
<p>
Les classes d'attentes ont une structure très simple.
Tellement simple qu'il devient très simple de créer
vos propres version de logique pour des tests utilisés couramment.
</p>
<p>
Par exemple voici la création d'une classe pour tester
la validité d'adresses IP. Pour fonctionner correctement
avec les bouchons serveurs et les objets fantaisie,
cette nouvelle classe d'attente devrait étendre
<span class="new_code">SimpleExpectation</span>...
<pre>
<strong>class ValidIp extends SimpleExpectation {
function test($ip) {
return (ip2long($ip) != -1);
}
function testMessage($ip) {
return "Address [$ip] should be a valid IP address";
}
}</strong>
</pre>
Il n'y a véritablement que deux méthodes à mettre en place.
La méthode <span class="new_code">test()</span> devrait renvoyer un <span class="new_code">true</span>
si l'attente doit passer, et une erreur <span class="new_code">false</span>
dans le cas contraire. La méthode <span class="new_code">testMessage()</span>
ne devrait renvoyer que du texte utile à la compréhension du test en lui-même.
</p>
<p>
Cette classe peut désormais être employée à la place
des classes d'attente précédentes.
</p>
<p><a class="target" name="unitaire"><h2>Sous le capot du testeur unitaire</h2></a></p>
<p>
Le <a href="http://sourceforge.net/projects/simpletest/">framework
de test unitaire SimpleTest</a> utilise aussi dans son coeur
des classes d'attente pour
la <a href="unit_test_documentation.html">classe UnitTestCase</a>.
Nous pouvons aussi tirer parti de ces mécanismes pour réutiliser
nos propres classes attente à l'intérieur même des suites de test.
</p>
<p>
La méthode la plus directe est d'utiliser la méthode
<span class="new_code">SimpleTest::assertExpectation()</span> pour effectuer le test...
<pre>
<strong>class TestOfNetworking extends UnitTestCase {
...
function testGetValidIp() {
$server = &new Server();
$this->assertExpectation(
new ValidIp(),
$server->getIp(),
'Server IP address->%s');
}
}</strong>
</pre>
C'est plutôt sale par rapport à notre syntaxe habituelle
du type <span class="new_code">assert...()</span>.
</p>
<p>
Pour un cas aussi simple, nous créons d'ordinaire une méthode
d'assertion distincte en utilisant la classe d'attente.
Supposons un instant que notre attente soit un peu plus
compliquée et que par conséquent nous souhaitions la réutiliser,
nous obtenons...
<pre>
class TestOfNetworking extends UnitTestCase {
...<strong>
function assertValidIp($ip, $message = '%s') {
$this->assertExpectation(new ValidIp(), $ip, $message);
}</strong>
function testGetValidIp() {
$server = &new Server();<strong>
$this->assertValidIp(
$server->getIp(),
'Server IP address->%s');</strong>
}
}
</pre>
Il est peu probable que nous ayons besoin
de ce niveau de contrôle sur la machinerie de test.
Il est assez rare que le besoin d'une attente dépasse
le stade de la reconnaissance d'un motif.
De plus, les classes d'attente complexes peuvent rendre
les tests difficiles à lire et à déboguer.
Ces mécanismes sont véritablement là pour les auteurs
de système qui étendront le framework de test
pour leurs propres outils de test.
</p>
</div>
References and related information...
<ul>
<li>
La page du projet SimpleTest sur
<a href="http://sourceforge.net/projects/simpletest/">SourceForge</a>.
</li>
<li>
La page de téléchargement de SimpleTest sur
<a href="http://www.lastcraft.com/simple_test.php">LastCraft</a>.
</li>
<li>
Les attentes imitent les contraintes dans
<a href="http://www.jmock.org/">JMock</a>.
</li>
<li>
<a href="http://simpletest.org/api/">L'API complète pour SimpleTest</a>
réalisé avec PHPDoc.
</li>
</ul>
<div class="menu_back"><div class="menu">
<a href="index.html">SimpleTest</a>
|
<a href="overview.html">Overview</a>
|
<a href="unit_test_documentation.html">Unit tester</a>
|
<a href="group_test_documentation.html">Group tests</a>
|
<a href="mock_objects_documentation.html">Mock objects</a>
|
<a href="partial_mocks_documentation.html">Partial mocks</a>
|
<a href="reporter_documentation.html">Reporting</a>
|
<a href="expectation_documentation.html">Expectations</a>
|
<a href="web_tester_documentation.html">Web tester</a>
|
<a href="form_testing_documentation.html">Testing forms</a>
|
<a href="authentication_documentation.html">Authentication</a>
|
<a href="browser_documentation.html">Scriptable browser</a>
</div></div>
<div class="copyright">
Copyright<br>Marcus Baker 2006
</div>
</body>
</html>
|