expectation_documentation.html 13.6 KB
Newer Older
zYne's avatar
zYne committed
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
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Documentation SimpleTest : &eacute;tendre le testeur unitaire avec des classes d'attentes suppl&eacute;mentaires</title>
<link rel="stylesheet" type="text/css" href="docs.css" title="Styles">
</head>
<body>
<div class="menu_back">
<div class="menu">
<h2>
<a href="index.html">SimpleTest</a>
</h2>
<ul>
<li>
<a href="overview.html">Overview</a>
</li>
<li>
<a href="unit_test_documentation.html">Unit tester</a>
</li>
<li>
<a href="group_test_documentation.html">Group tests</a>
</li>
<li>
<a href="server_stubs_documentation.html">Server stubs</a>
</li>
<li>
<a href="mock_objects_documentation.html">Mock objects</a>
</li>
<li>
<a href="partial_mocks_documentation.html">Partial mocks</a>
</li>
<li>
<a href="reporter_documentation.html">Reporting</a>
</li>
<li>
<a href="expectation_documentation.html">Expectations</a>
</li>
<li>
<a href="web_tester_documentation.html">Web tester</a>
</li>
<li>
<a href="form_testing_documentation.html">Testing forms</a>
</li>
<li>
<a href="authentication_documentation.html">Authentication</a>
</li>
<li>
<a href="browser_documentation.html">Scriptable browser</a>
</li>
</ul>
</div>
</div>
<h1>Documentation sur les attentes</h1>
<div class="content">
        <p>
<a class="target" name="fantaisie">
<h2>Plus de contr&ocirc;le sur les objets fantaisie</h2>
</a>
</p>
            <p>
                Le comportement par d&eacute;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&eacute;cessaire de ramollir un sc&eacute;nario de test.
            </p>
            <p>
                Un des endroits o&ugrave; un test peut &ecirc;tre trop serr&eacute; 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&eacute;e, mais le texte proprement dit risque d'&ecirc;tre plut&ocirc;t long. Si vous testez le texte dans son ensemble alors &agrave; chaque modification de ce m&ecirc;me message -- m&ecirc;me un point ou une virgule -- vous aurez &agrave; revenir sur la suite de test pour la modifier.
            </p>
            <p>
                Voici un cas concret, nous avons un service d'actualit&eacute;s qui a &eacute;chou&eacute; dans sa tentative de connexion &agrave; sa source distante.
<pre>
<strong>class NewsService {
    ...
    function publish(&amp;$writer) {
        if (! $this-&gt;isConnected()) {
            $writer-&gt;write('Cannot connect to news service "' .
                    $this-&gt;_name . '" at this time. ' .
                    'Please try again later.');
        }
        ...
    }
}</strong>
</pre>
                L&agrave; 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 = &amp;new MockWriter($this);
        $writer-&gt;expectOnce('write', array(
                'Cannot connect to news service ' .
                '"BBC News" at this time. ' .
                'Please try again later.'));
        
        $service = &amp;new NewsService('BBC News');
        $service-&gt;publish($writer);
        
        $writer-&gt;tally();</strong>
    }
}
</pre>
                C'est un bon exemple d'un test fragile. Si nous d&eacute;cidons d'ajouter des instructions compl&eacute;mentaires, par exemple proposer une source d'actualit&eacute;s alternative, nous casserons nos tests par la m&ecirc;me occasion sans pourtant avoir modifi&eacute; une seule fonctionnalit&eacute;.
            </p>
            <p>
                Pour contourner ce probl&egrave;me, nous voudrions utiliser un test avec une expression rationnelle plut&ocirc;t qu'une correspondance exacte. Nous pouvons y parvenir avec...
<pre>
class TestOfNewsService extends UnitTestCase {
    ...
    function testConnectionFailure() {
        $writer = &amp;new MockWriter($this);<strong>
        $writer-&gt;expectOnce(
                'write',
                array(new WantedPatternExpectation('/cannot connect/i')));</strong>
        
        $service = &amp;new NewsService('BBC News');
        $service-&gt;publish($writer);
        
        $writer-&gt;tally();
    }
}
</pre>
                Plut&ocirc;t que de transmettre le param&egrave;tre attendu au <span class="new_code">MockWriter</span>, nous envoyons une classe d'attente appel&eacute;e <span class="new_code">WantedPatternExpectation</span>. L'objet fantaisie est suffisamment &eacute;l&eacute;gant pour reconna&icirc;tre qu'il s'agit d'un truc sp&eacute;cial et pour le traiter diff&eacute;remment. Plut&ocirc;t que de comparer l'argument entrant &agrave; cet objet, il utilise l'objet attente lui-m&ecirc;me pour ex&eacute;cuter le test.
            </p>
            <p>
                <span class="new_code">WantedPatternExpectation</span> utilise l'expression rationnelle pour la comparaison avec son constructeur. A chaque fois q'une comparaison est fait &agrave; travers <span class="new_code">MockWriter</span> par rapport &agrave; cette classe attente, elle fera un <span class="new_code">preg_match()</span> avec ce motif. Dans notre sc&eacute;nario de test ci-dessus, aussi longtemps que la cha&icirc;ne "cannot connect" appara&icirc;t dans le texte, la fantaisie transmettra un succ&egrave;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 &eacute;galit&eacute;, plut&ocirc;t que la plus forte comparaison &agrave; l'identique</td>
</tr>
                    <tr>
<td><span class="new_code">NotEqualExpectation</span></td><td>Une comparaison sur la non-&eacute;galit&eacute;</td>
</tr>
                    <tr>
<td><span class="new_code">IndenticalExpectation</span></td><td>La v&eacute;rification par d&eacute;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&icirc;ne</td>
</tr>
                    <tr>
<td><span class="new_code">NoUnwantedPatternExpectation</span></td><td>Passe seulement si l'expression rationnelle Perl &eacute;choue</td>
</tr>
                    <tr>
<td><span class="new_code">IsAExpectation</span></td><td>V&eacute;rifie le type ou le nom de la classe uniquement</td>
</tr>
                    <tr>
<td><span class="new_code">NotAExpectation</span></td><td>L'oppos&eacute; de <span class="new_code">IsAExpectation</span></td>
</tr>
                    <tr>
<td><span class="new_code">MethodExistsExpectation</span></td><td>V&eacute;rifie si la m&eacute;thode est disponible sur un objet</td>
</tr>
                </tbody>
</table>
                La plupart utilisent la valeur attendue dans le constructeur. Les exceptions sont les v&eacute;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&icirc;ne.
            </p>
        
        <p>
<a class="target" name="comportement">
<h2>Utiliser les attentes pour contr&ocirc;ler les bouchons serveur</h2>
</a>
</p>
            <p>
                Les classes attente peuvent servir &agrave; 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> our celui d'un <a href="server_stubs_documentation.html">bouchon serveur</a>. A chaque fois qu'une liste d'arguments est donn&eacute;e, une liste d'objets attente peut &ecirc;tre ins&eacute;r&eacute;e &agrave; la place.
            </p>
            <p>
                Mettons que nous voulons qu'un bouchon serveur d'autorisation simule une connexion r&eacute;ussie seulement si il re&ccedil;oit un objet de session valide. Nous pouvons y arriver avec ce qui suit...
<pre>
Stub::generate('Authorisation');
<strong>
$authorisation = new StubAuthorisation();
$authorisation-&gt;setReturnValue(
        'isAllowed',
        true,
        array(new IsAExpectation('Session', 'Must be a session')));
$authorisation-&gt;setReturnValue('isAllowed', false);</strong>
</pre>
                Le comportement par d&eacute;faut du bouchon serveur est d&eacute;fini pour renvoyer <span class="new_code">false</span> quand <span class="new_code">isAllowed</span> est appel&eacute;. Lorsque nous appelons cette m&eacute;thode avec un unique param&egrave;tre qui est un objet <span class="new_code">Session</span>, il renverra <span class="new_code">true</span>. Nous avons aussi ajout&eacute; un deuxi&egrave;me param&egrave;tre comme message. Il sera affich&eacute; dans le message d'erreur de l'objet fantaisie si l'attente est la cause de l'&eacute;chec.
            </p>
            <p>
                Ce niveau de sophistication est rarement utile : il n'est inclut que pour &ecirc;tre complet.
            </p>
        
        <p>
<a class="target" name="etendre">
<h2>Cr&eacute;er vos propres attentes</h2>
</a>
</p>
            <p>
                Les classes d'attentes ont une structure tr&egrave;s simple. Tellement simple qu'il devient tr&egrave;s simple de cr&eacute;er vos propres version de logique pour des tests utilis&eacute;s couramment.
            </p>
            <p>
                Par exemple voici la cr&eacute;ation d'une classe pour tester la validit&eacute; d'adresses IP. Pour fonctionner correctement avec les bouchons serveurs et les objets fantaisie, cette nouvelle classe d'attente devrait &eacute;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&eacute;ritablement que deux m&eacute;thodes &agrave; mettre en place. La m&eacute;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&eacute;thode <span class="new_code">testMessage()</span> ne devrait renvoyer que du texte utile &agrave; la compr&eacute;hension du test en lui-m&ecirc;me.
            </p>
            <p>
                Cette classe peut d&eacute;sormais &ecirc;tre employ&eacute;e &agrave; la place des classes d'attente pr&eacute;c&eacute;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/">frameworkd 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&eacute;canismes pour r&eacute;utiliser nos propres classes attente &agrave; l'int&eacute;rieur m&ecirc;me des suites de test.
            </p>
            <p>
                La m&eacute;thode la plus directe est d'utiliser la m&eacute;thode <span class="new_code">SimpleTest::assertExpectation()</span> pour effectuer le test...
<pre>
<strong>class TestOfNetworking extends UnitTestCase {
    ...
    function testGetValidIp() {
        $server = &amp;new Server();
        $this-&gt;assertExpectation(
                new ValidIp(),
                $server-&gt;getIp(),
                'Server IP address-&gt;%s');
    }
}</strong>
</pre>
                C'est plut&ocirc;t sale par rapport &agrave; notre syntaxe habituelle du type <span class="new_code">assert...()</span>.
            </p>
            <p>
                Pour un cas aussi simple, nous cr&eacute;ons d'ordinaire une m&eacute;thode d'assertion distincte en utilisant la classe d'attente. Supposons un instant que notre attente soit un peu plus compliqu&eacute;e et que par cons&eacute;quent nous souhaitions la r&eacute;utiliser, nous obtenons...
<pre>
class TestOfNetworking extends UnitTestCase {
    ...<strong>
    function assertValidIp($ip, $message = '%s') {
        $this-&gt;assertExpectation(new ValidIp(), $ip, $message);
    }</strong>
    
    function testGetValidIp() {
        $server = &amp;new Server();<strong>
        $this-&gt;assertValidIp(
                $server-&gt;getIp(),
                'Server IP address-&gt;%s');</strong>
    }
}
</pre>
                Il est peu probable que nous ayons besoin de ce niveau de contr&ocirc;le sur la machinerie de test. Il est assez rare que le besoin d'une attente d&eacute;passe le stade de la reconnaissance d'un motif. De plus, les classes d'attente complexes peuvent rendre les tests difficiles &agrave; lire et &agrave; d&eacute;boguer. Ces m&eacute;canismes sont v&eacute;ritablement l&agrave; pour les auteurs de syst&egrave;me qui &eacute;tendront le framework de test pour leurs propres outils de test.
            </p>
        
    </div>
<div class="copyright">
            Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004
        </div>
</body>
</html>