<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Blog de Bruno Orsier &#187; Tests unitaires</title>
	<atom:link href="https://blog.developpez.com/bruno-orsier/pcategory/tests-unitaires/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.developpez.com/bruno-orsier</link>
	<description></description>
	<lastBuildDate>Thu, 04 Apr 2013 16:06:21 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.42</generator>
	<item>
		<title>Des chiffres sur un de nos projets !</title>
		<link>https://blog.developpez.com/bruno-orsier/p8761/developpement-agile/des_chiffres_sur_un_de_nos_projets</link>
		<comments>https://blog.developpez.com/bruno-orsier/p8761/developpement-agile/des_chiffres_sur_un_de_nos_projets#comments</comments>
		<pubDate>Fri, 26 Mar 2010 14:25:28 +0000</pubDate>
		<dc:creator><![CDATA[Bruno Orsier]]></dc:creator>
				<category><![CDATA[Développement agile]]></category>
		<category><![CDATA[Tests unitaires]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[J&#8217;aime bien lire des billets qui contiennent des informations factuelles sur de vrais projets, car cela permet de se situer par rapport à d&#8217;autres équipes et entreprises. Récemment j&#8217;ai vu notamment le billet Démarches de tests fonctionnels, de Christian Blavier, qui citait des chiffres intéressants (dans les commentaires du billet) : Voici quelques métriques que je viens de réunir dans différents contextes : - secteur media, sur un projet avec 3 développeurs qui dure depuis [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>J&rsquo;aime bien lire des billets qui contiennent des informations factuelles sur de vrais projets, car cela permet de se situer par rapport à d&rsquo;autres équipes et entreprises. Récemment j&rsquo;ai vu notamment le billet <a href="http://blog.octo.com/demarches-de-tests-fonctionnels/">Démarches de tests fonctionnels</a>, de Christian Blavier, qui citait des chiffres intéressants (dans les commentaires du billet) :</p>
<blockquote><p>Voici quelques métriques que je viens de réunir dans différents contextes :      <br />- secteur media, sur un projet avec 3 développeurs qui dure depuis 9 mois on a plus de 100 scénarios de test GreenPepper       <br />- secteur media, plusieurs équipes de 3 développeurs, ils ajoutaient en moyenne 5 tests fitnesse par itération de 2 semaines, soit plus d&rsquo;une centaine de tests par an       <br />- encore media, équipe de 7 développeurs avec 2 MOA à plein temps et qui font des tests GP depuis 18 mois : 8743 assertions (dans 122 pages) soit 153 assertions en moyenne par itération d&rsquo;une semaine       <br />- secteur assurance, une équipe de 2 développeurs + 1 MOA sur 6 mois pour tester une plateforme de services (20 web services) : 200 pages de tests Fitnesse et 5000 assertions. Un retour d&rsquo;exp complet est disponible ici : <a href="http://usi2008.universite-du-si.com/WebcastArchi.aspx">http://usi2008.universite-du-si.com/WebcastArchi.aspx</a> (session A10)       <br />- secteur open source <img alt=";-)" src="http://blog.octo.com/wp-includes/images/smilies/icon_wink.gif" /> , octopus microfinance et son harnais de test GP disponible ici : <a href="http://wiki.octopusnetwork.org/display/OPUS/Home">http://wiki.octopusnetwork.org/display/OPUS/Home</a></p>
</blockquote>
<p>Dans ces mêmes commentaires, Louis Pellerin ajoutait :</p>
<blockquote><p>De mon expérience avec GreenPepper pour le produit Talia que nous développons (un produit sans réelle interface utilisateur), nous avions une proportion d&rsquo;environ 80 spécifications exécutables (GreenPepper) pour plus de 200 tests unitaires et intégrés (mstest).</p>
</blockquote>
<p>Il y a aussi le billet <a href="http://pyxis-tech.com/blog/2010/02/23/pendant-ce-temps-la-a-boulogne/">Pendant ce temps-là, à Boulogne.</a>, d&rsquo;Emmanuel Gaillot, qui donne les chiffres suivants :</p>
<blockquote><p>L&rsquo;équipe compte actuellement 6 programmeurs (dont le responsable produit), 2 graphistes, 3 modérateurs et une poignée de <em>stake-holders</em>. Tout ce monde travaille dans un ancien atelier réhabilité à Boulogne, dans une même pièce. L&rsquo;application web est développée en <a href="http://fr.wikipedia.org/wiki/Test_Driven_Development">TDD</a> (à l&rsquo;exception des vues). Elle est actuellement couverte par 951 assertions, réparties sur 563 tests automatisés qui s&rsquo;exécutent en 35 secondes environ.</p>
<p>En 4 mois de boulot, nous avons consommé 150 pages de flipchart, 15 marqueurs, 100 fiches cartonnées, 2000 post-its de couleurs et tailles variées, 10 stylos feutre, 5 stylos bic, 2 bloc-notes, 50 feuilles A4, un playmobil et 300 viennoiseries.</p>
</blockquote>
<p>Si vous connaissez d&rsquo;autres billets ou articles du même style je suis preneur. </p>
<p>En attendant voici quelques informations du même type sur l&rsquo;un de nos projets : <strong>38 sprints</strong> de développement (durée 3 semaines), une équipe de développement de (en moyenne) 4 développeurs et 1 chimiste testeur/représentant des utilisateurs (plus 1 ScrumMaster, et 1 Product Owner).</p>
<p><span id="more-29"></span></p>
<p>L&rsquo;équipe a réalisé un total de <strong>837 tests</strong> :</p>
<ul>
<li>
<p>469 tests unitaires  </p>
</li>
<li>
<p>235 tests d&rsquo;intégration</p>
</li>
<li>
<p>132 tests fonctionnels (81 manuels et 51 automatiques)</p>
</li>
</ul>
<p>ce qui représente <strong>84% de tests (unitaires + intégration) et 16% de tests fonctionnels, </strong>mais encore <strong>90% de tests automatiques. </strong>Les tests fonctionnels manuels coûtent seulement <strong>2-3 heures</strong> de validation à l&rsquo;équipe, qui peut donc les jouer plusieurs fois par sprint si besoin.</p>
<p>Tous ces tests sont le résultat d&rsquo;un effort permanent de toute l&rsquo;équipe : TDD, automatisation des tests fonctionnels, &laquo;&nbsp;refactoring&nbsp;&raquo; permanent des tests fonctionnels.</p>
<p>Les tests unitaires et d&rsquo;intégration représentent environ 3000 assertions. Ils sont exécutés avec <a href="http://dunit.sourceforge.net/">DUnit</a>.</p>
<p>Voici un graphique montrant l&rsquo;évolution de la répartition des tests au fil du temps, sur les sprints les plus récents :</p>
<p><a href="http://ftp-developpez.com/bruno-orsier/blog/images/243e33ee334c_EDB7/image.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://ftp-developpez.com/bruno-orsier/blog/images/243e33ee334c_EDB7/image_thumb.png" width="644" height="234" /></a> </p>
<p> </p>
<p>Voici la légende <a href="http://ftp-developpez.com/bruno-orsier/blog/images/243e33ee334c_EDB7/image_3.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://ftp-developpez.com/bruno-orsier/blog/images/243e33ee334c_EDB7/image_thumb_3.png" width="121" height="179" /></a> et la répartition finale <a href="http://ftp-developpez.com/bruno-orsier/blog/images/243e33ee334c_EDB7/image_4.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://ftp-developpez.com/bruno-orsier/blog/images/243e33ee334c_EDB7/image_thumb_4.png" width="304" height="179" /></a> (où j&rsquo;ai regroupé tests fonctionnels automatiques et manuels en une seule catégorie).</p>
<p>Cette répartition paraît assez satisfaisante : on a bien une <strong>pyramide</strong> de tests, il y plus de tests unitaires que de tests d&rsquo;intégration, et plus de tests d&rsquo;intégration que de tests fonctionnels. D&rsquo;autre part le nombre de tests fonctionnels manuels est resté sous contrôle, et c&rsquo;était l&rsquo;un des objectifs de l&rsquo;équipe de pouvoir exécuter tous leurs tests manuels en moins d&rsquo;une demi-journée ; le graphique montre d&rsquo;ailleurs qu&rsquo;en cours de route, certains tests manuels ont été remplacés par des tests automatiques, afin de ne pas se laisser déborder par les tests manuels.</p>
</p>
<p>C&rsquo;est un projet qui compte environ 180.000 lignes de code Delphi. C&rsquo;est un projet de taille moyenne pour nous, nous avons des projets beaucoup plus petits, et de plus rares projets 5 ou 6 fois plus gros.</p>
<p>Sur ce projet, comme sur un projet précédent d&rsquo;ailleurs, nous avions décidé de maîtriser la <a href="http://fr.wikipedia.org/wiki/Nombre_cyclomatique">complexité cyclomatique</a> (nous étions motivés par la constatation sur d&rsquo;anciens projets que des méthodes de complexité 50 voire 70 étaient de véritables nids à bugs critiques). Le graphique ci-dessous montre sur les sprints les plus récents l&rsquo;évolution du nombre total de méthodes (en bleu), et l&rsquo;évolution du nombre total de méthodes dont la complexité cyclomatique est supérieure à 7 (notre limite). <strong>Sur ce projet, nous avons donc programmé plus de 6200 méthodes de complexité inférieure à 7</strong>. Toutefois nous tolérons l&rsquo;existence de quelques dizaines de méthodes complexes car le bénéfice de baisser leur complexité ne nous semble pas assez important. Mais ces méthodes complexes ne représentent que 0.6% du nombre de total de méthodes ! </p>
<p><a href="http://ftp-developpez.com/bruno-orsier/blog/images/243e33ee334c_EDB7/image_5.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://ftp-developpez.com/bruno-orsier/blog/images/243e33ee334c_EDB7/image_thumb_5.png" width="644" height="312" /></a> </p>
</p>
<p>La définition de la limite acceptable (7 dans notre cas) peut donner lieu à bien des débats, et vous trouverez fréquemment des recommandations comme 15 ou 25. Mais à mon avis, même une limite de 7 vous donne bien des opportunités de coder des bugs, car cela vous laisse libre de coder suffisamment de<em> if, while, for et conditions booléennes</em> tous ensemble. Donc pour moi il est important de s&rsquo;imposer une limite assez basse pour vraiment tirer parti de la réduction de la complexité.</p>
<p>Ces données illustrent deux axes sur lesquels nous nous sommes bien améliorés ces dernières années : maîtrise des tests, maîtrise de la complexité du code. Et qu&rsquo;en est-il de la satisfaction des clients sur ce projet ? Eh bien c&rsquo;est assez embarrassant, je ne sais pas encore quelle est la réponse ! En effet, en nous améliorant, nous avons essentiellement <strong>déplacé le goulet d&rsquo;étranglement</strong> dans d&rsquo;autres secteurs de l&rsquo;entreprise qui actuellement peinent à absorber notre production, et à livrer leur part du travail (pourtant ils ont vu passer plus de 30 livraisons qui fonctionnaient, ils n&rsquo;auraient pas dû être surpris, non ?). </p>
<p>Après tout, rien de très surprenant, c&rsquo;est en gros ce que prédit la théorie des contraintes.  Mais cela indique que dans de futurs projets, tout en continuant à nous améliorer sur d&rsquo;autres axes (comme <a href="http://blog.developpez.com/bruno-orsier/p8534/developpement-agile/pourquoi-sarsquo-interesser-au-bdd-behav/">travailler avec des exemples et pratiquer le BDD</a>), il va falloir travailler à introduire de l&rsquo;agilité dans tous les secteurs qui contribuent à la livraison d&rsquo;un produit chez des clients ! Nous atteignons peut-être les limites de ce que nous pouvons faire avec des améliorations locales. </p>
<p><em>Merci à mes collègues Sandrine et Jean pour leur aide dans la collecte des données.</em></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Du BDD vers le TDD, et vice-versa</title>
		<link>https://blog.developpez.com/bruno-orsier/p8594/tests-unitaires/du_bdd_vers_le_tdd_et_vice_versa</link>
		<comments>https://blog.developpez.com/bruno-orsier/p8594/tests-unitaires/du_bdd_vers_le_tdd_et_vice_versa#comments</comments>
		<pubDate>Thu, 04 Feb 2010 16:00:26 +0000</pubDate>
		<dc:creator><![CDATA[Bruno Orsier]]></dc:creator>
				<category><![CDATA[BDD]]></category>
		<category><![CDATA[Tests unitaires]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Avant de passer au compte-rendu du dojo d&#8217;aujourd&#8217;hui, voici un nouvel éclairage sur l&#8217;articulation entre TDD et BDD, que j&#8217;emprunte à ce billet Behavior Driven Development with NBehave (trouvé grâce à cet autre billet Bien Tester une application Asp.net MVC sur le BDD par Guillaume Saint Etienne). Je pense que dans les dojos précédents nous avons bien compris et bien pratiqué le cycle RED/GREEN/REFACTOR du TDD : Nous voulons maintenant comprendre comment cela s&#8217;articule avec [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Avant de passer au compte-rendu du dojo d&rsquo;aujourd&rsquo;hui, voici un nouvel éclairage sur l&rsquo;articulation entre TDD et BDD, que j&rsquo;emprunte à ce billet <a href="http://pebblesteps.com/post/Behavior-Driven-Development-with-NBehave.aspx" target="_blank">Behavior Driven Development with NBehave</a> (trouvé grâce à cet autre billet <a href="http://www.dotnetguru2.org/gse/index.php/2010/01/21/bien-tester-une-application-asp-net-mvc" target="_blank">Bien Tester une application Asp.net MVC</a> sur le BDD par Guillaume Saint Etienne).</p>
<p>Je pense que dans les dojos précédents nous avons bien compris et bien pratiqué le cycle RED/GREEN/REFACTOR du TDD :</p>
<p><a href="http://pebblesteps.com/post/Behavior-Driven-Development-with-NBehave.aspx"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://ftp-developpez.com/bruno-orsier/blog/images/e64affed95e6_8F87/image4.png" width="248" height="46" /></a> </p>
<p>Nous voulons maintenant comprendre comment cela s&rsquo;articule avec le BDD (partant du principe que nous sommes convaincus que cela vaut le coup de <a href="http://blog.developpez.com/bruno-orsier/p8534/developpement-agile/pourquoi-sarsquo-interesser-au-bdd-behav/" target="_blank">s&rsquo;intéresser au BDD</a>). </p>
<p>Je trouve que le schéma ci-dessous de <a href="http://pebblesteps.com/post/Behavior-Driven-Development-with-NBehave.aspx" target="_blank">Behavior Driven Development with NBehave</a> illustre assez bien la manière de procéder, du BDD vers le TDD :</p>
<p><a href="http://pebblesteps.com/post/Behavior-Driven-Development-with-NBehave.aspx" target="_blank"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://ftp-developpez.com/bruno-orsier/blog/images/e64affed95e6_8F87/image.png" width="456" height="106" /></a> </p>
<p><span id="more-47"></span></p>
<p>Voici comment je comprends ce schéma :</p>
<ul>
<li>On se laisse donc guider par une user story (ce qui évite un risque du TDD qui est de s&rsquo;égarer sur des questions peu pertinentes pour l&rsquo;utilisateur final, étant donné que l&rsquo;on travaille très près du code). </li>
<li>La première étape est d&rsquo;avoir des scénarios &laquo;&nbsp;Pending&nbsp;&raquo; &#8211; en Cucumber cela correspond au premier fragment de pont que Cucumber génère automatiquement. </li>
<li>En implémentant ces scénarios, on définit itérativement l&rsquo;API des classes métier. Pour cela on suit un cycle RED/GREEN/REFACTOR au niveau de l&rsquo;outil de BDD, et on ne cherche pas nécessairement à implémenter le comportement réel et final : on peut utiliser des comportements &laquo;&nbsp;codés en dur&nbsp;&raquo;, ou simplistes &#8211; c&rsquo;est ce qu&rsquo;illustrait le dojo précédent finalement. C&rsquo;est la partie &laquo;&nbsp;Stubbed Scenario&nbsp;&raquo; ci-dessus. </li>
<li>Une fois que l&rsquo;API est stable, on implémente le comportement réel, et pour cela on pratique le RED/GREEN/REFACTOR en changeant d&rsquo;outil : cette fois on fait du TDD avec un outil du type xUnit. </li>
<li>Et l&rsquo;on obtient un test d&rsquo;acceptance (sous forme de scénario exécutable) à la fin du processus. </li>
</ul>
<p>Aujourd&rsquo;hui dans le dojo nous avons travaillé essentiellement au niveau &laquo;&nbsp;Real Behavior&nbsp;&raquo;, l&rsquo;API de notre classe <em>CalibrationCurve</em> avait en effet été stabilisée grâce au travail de la dernière fois, nous n&rsquo;avons pas eu à la retoucher. Par contre nous avons complètement reprogrammé son comportement interne.</p>
<p>Pour commencer à programmer, nous nous sommes appuyés sur le test unitaire suivant :</p>
<div id="codeSnippetWrapper" class="csharpcode-wrapper">
<pre id="codeSnippet" class="csharpcode"><span class="kwrd">class</span> LinearRegressionTest ? Test::Unit::TestCase<br /><br />  def setup<br />    @curve = CalibrationCurve.<span class="kwrd">new</span><br />    @curve.ajoute_points(0, 10)<br />    @curve.ajoute_points(10, 100)<br />  end<br /><br />  def test_calcul_slope<br />    assert_in_delta(0.111, @curve.calcule_slope, 0.001)<br />  end<br /><br />  def test_calcule_ordinate<br />    assert_in_delta(-1.111, @curve.calcule_ordinate, 0.001)<br />  end<br /><br />end<br /></pre>
<p></div>
<p><em><font size="2">(désolé, pour d&rsquo;obscures raisons le moteur de blog ne me laisse pas insérer le caractère &laquo;&nbsp;inférieur&nbsp;&raquo;, et j&rsquo;ai mis &#8804; à la place dans les morceaux de code qui en avaient besoin)</font></em></p>
<p>On peut noter que nous n&rsquo;avons pas inventé de nouvelles données du test, nous avons simplement utilisé les données déjà présentes dans le scénario qui échouait à la fin de la dernière séance :</p>
<p><img src="http://ftp-developpez.com/bruno-orsier/blog/images/1180c6cc8ff6_A297/image_5.png" /> </p>
<p>Avec le recul, on peut se demander si ce test unitaire n&rsquo;est pas complètement redondant avec le scénario, et s&rsquo;il présente vraiment de l&rsquo;intérêt ! En effet nous exerçons l&rsquo;API de notre classe exactement comme dans le pont entre le scénario et la classe CalibrationCurve. </p>
<p>Par contre, en programmant les calculs nous avons identifié un cas limite (un seul point fourni par l&rsquo;utilisateur) et nous avons dû prendre une décision sur le comportement attendu. Voici un test unitaire qui couvre ce cas :</p>
<div id="codeSnippetWrapper" class="csharpcode-wrapper">
<pre id="codeSnippet" class="csharpcode"><span class="kwrd">class</span> LinearRegressionTestLimite ? Test::Unit::TestCase<br /><br />  def setu&lt;<br />    @curve = CalibrationCurve.<span class="kwrd">new</span><br />    @curve.ajoute_points(0, 0)<br />  end<br /><br />  def test_slope_should_be_zero<br />    assert_equal(@curve.calcule_slope, 0)<br />  end<br /><br />  def test_ordinate_should_be_zero<br />      assert_equal(@curve.calcule_ordinate, 0)<br />    end<br /><br />end</pre>
<p></div>
<p>On peut alors se demander si cette information doit rester &laquo;&nbsp;cachée&nbsp;&raquo; dans un test unitaire, ou bien s&rsquo;il faut la faire remonter à l&rsquo;utilisateur, via un complément de scénario. La décision dépend surement d&rsquo;un dialogue avec les utilisateurs : est-ce que ce cas limite est pertinent pour eux ou non ? est-ce qu&rsquo;ils sont intéressés par spécifier le comportement attendu ?</p>
<p>En tout cas ce petit exemple nous indique déjà que le schéma ci-dessus <strong>Story ==> Acceptance Test</strong> n&rsquo;est pas si linéaire que cela : <strong><font color="#ff0000">en travaillant en TDD, nous pouvons découvrir des compléments de scénarios !</font></strong></p>
<p>Sur un point plus technique, nous avons également éprouvé le besoin d&rsquo;un test unitaire pour nous assurer du bon fonctionnement de notre extension du module Enumerable, auquel nous avons ajouté une méthode <em>mean</em> comme ci-dessous :</p>
<div id="codeSnippetWrapper" class="csharpcode-wrapper">
<pre id="codeSnippet" class="csharpcode">module Enumerable<br />  def mean<br />    sum = self.inject(0.0) <span class="kwrd">do</span> |sum, xi|<br />      sum = sum + xi<br />    end<br />    <span class="kwrd">return</span> sum / self.size<br />  end  <br />end</pre>
<p></div>
<p>Comme nous ne connaissions pas suffisamment le principe du <em>inject</em>, le test unitaire nous a rassuré sur le bon fonctionnement de notre extension <em>mean</em> :</p>
<div id="codeSnippetWrapper" class="csharpcode-wrapper">
<pre id="codeSnippet" class="csharpcode"><span class="kwrd">class</span> EnumerableTest ? Test::Unit::TestCase<br /><br />  def setup<br />    @array = [10, 100]<br />  end<br /><br />  def test_mean<br />    assert_equal(55, @array.mean)<br />  end  <br />end</pre>
<p></div>
<p>En ce qui me concerne, je garderais comme test unitaire uniquement ce dernier point très technique, et je me contenterais du scénario et d&rsquo;un complément de scénario pour le cas limite. Et vous, comment feriez-vous ?</p>
<p>Pour finir, le code de <em>CalibrationCurve</em> qui passe avec succès nos scénarios et tests unitaires :</p>
<div id="codeSnippetWrapper" class="csharpcode-wrapper">
<pre id="codeSnippet" class="csharpcode"><span class="kwrd">class</span> CalibrationCurve<br /><br />  def initialize<br />    @x = []<br />    @y = []<br />  end<br /><br />  def compute<br />    xbar = @x.mean   <br />    ybar = @y.mean<br />    sx2= @x.map{|xi| (xi-xbar)**2}.mean<br /><br />    sum = 0<br />    @x.each_with_index <span class="kwrd">do</span> |xi, i|<br />      sum = sum + (@x[i]-xbar)*(@y[i]-ybar)<br />    end<br />    sxy = sum / @x.size<br /><br />    <span class="kwrd">if</span> sx2.abs != 0 then<br />      @a = sxy / sx2<br />      @b = ybar - @a*xbar<br />    <span class="kwrd">else</span><br />      @a = 0<br />      @b = 0<br />    end<br />  end<br /><br />  def ajoute_points (area,concentration)<br />    @x.push(concentration)<br />    @y.push(area)<br /><br />    compute<br />  end<br /><br />  def calcul_concentration(area)<br />    <span class="kwrd">return</span> (area - @b) / @a<br />  end<br /><br />  def calcule_slope<br />    <span class="kwrd">return</span> @a<br />  end<br /><br />  def calcule_ordinate<br />    <span class="kwrd">return</span> @b<br />  end<br /><br />end</pre>
<p></div>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Dojos et apprentissage de bonnes pratiques : les noms de tests</title>
		<link>https://blog.developpez.com/bruno-orsier/p7291/tests-unitaires/dojos_et_apprentissage_de_bonnes_pratiqu</link>
		<comments>https://blog.developpez.com/bruno-orsier/p7291/tests-unitaires/dojos_et_apprentissage_de_bonnes_pratiqu#comments</comments>
		<pubDate>Thu, 26 Feb 2009 22:29:21 +0000</pubDate>
		<dc:creator><![CDATA[Bruno Orsier]]></dc:creator>
				<category><![CDATA[Bonnes pratiques]]></category>
		<category><![CDATA[Programmation]]></category>
		<category><![CDATA[Tests unitaires]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Une bonne pratique qui est apparue très rapidement dans nos dojos de programmation est de bien nommer les tests. En effet, comme les dojos successifs sont séparés de plusieurs semaines, à chaque fois on redécouvre le code, et quand les tests sont mal nommés, nous sommes obligés de les relire entièrement pour les comprendre. En fait l&#8217;un des avantages des dojos est de simuler sur une courte période ce qui se passe dans la vie [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Une bonne pratique qui est apparue très rapidement dans nos dojos de programmation est de <strong>bien nommer les tests</strong>. En effet, comme les dojos successifs sont séparés de plusieurs semaines, à chaque fois on redécouvre le code, et quand les tests sont mal nommés, nous sommes obligés de les relire entièrement pour les comprendre. En fait l&rsquo;un des avantages des dojos est de simuler sur une courte période ce qui se passe dans la vie réelle : quand vous revenez sur votre code plusieurs mois ou années plus tard, vous avez parfois l&rsquo;impression qu&rsquo;il a été écrit par quelqu&rsquo;un d&rsquo;autre, non ?</p>
<p>Par conséquent les participants des dojos sont tous d&rsquo;accord sur le fait qu&rsquo;il faut bien nommer les tests. Rien de très surprenant, me direz-vous, c&rsquo;est une bonne pratique bien connue. Par exemple dans <a href="http://www.amazon.fr/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/" target="_blank">Clean Code</a>, le livre de l&rsquo;oncle Bob dont j&rsquo;ai parlé dans un <a href="http://blog.developpez.com/bruno-orsier/p7221/bonnes-pratiques/programmation/les-commentaires-traduisent-notre-echec-/" target="_blank">billet précédent</a>, il y a plusieurs points qui parlent du nommage correct. Le deuxième chapitre, écrit par <a href="http://www.objectmentor.com/omTeam/ottinger_t.html" target="_blank">Tim Ottinger</a>, est même entièrement consacré à la question du nommage. Plus loin dans le livre, le chapitre &laquo;&nbsp;Tests propres&nbsp;&raquo; mentionne notamment :</p>
<p><span id="more-35"></span></p>
<blockquote><p>Qu&rsquo;est-ce qui rend un test propre ? Trois choses. Lisibilité, lisibilité et lisibilité. La lisibilité est même peut-être plus importante dans les tests unitaires que dans le code de production. Qu&rsquo;est-ce qui rend les tests lisibles ? La même chose qui rend tout code lisible : clarté, simplicité et densité d&rsquo;expression. Dans un test vous voulez dire beaucoup avec aussi peu d&rsquo;expressions que possible. [&#8230;]</p>
</blockquote>
<p>La lisibilité du nom du test est donc importante pour avoir un test &laquo;&nbsp;propre&nbsp;&raquo;. Enfin dans le chapitre &laquo;&nbsp;Odeurs et heuristiques&nbsp;&raquo;, il y a plusieurs points sur les noms, comme :</p>
<blockquote><p>N1 : choisissez des noms descriptifs</p>
<p>N&rsquo;allez pas trop vite pour choisir un nom. Assurez-vous que le nom est descriptif. Rappelez-vous que les significations tendent à dériver au fur et à mesure que le logiciel évolue, donc ré-évaluez fréquemment les noms que vous choisissez.</p>
<p>Ce n&rsquo;est pas simplement une recommandation pour faire joli. Dans le logiciel les noms constituent 90% de ce qui rend le code lisible. Vous devez prendre le temps de les choisir soigneusement and de les garder pertinents. Les noms sont trop importants pour être traités à la légère.</p>
<p>[&#8230;]</p>
<p>N4 : des noms non-ambigus</p>
<p>Choisissez des noms qui rendent non-ambigus le fonctionnement d&rsquo;une fonction ou d&rsquo;une variable. [&#8230;]</p>
</blockquote>
<p>Pour l&rsquo;instant je n&rsquo;ai pas prêté suffisamment attention à cette question des noms des tests. J&rsquo;utilisais le premier nom qui me venait à l&rsquo;esprit. Suite à la &laquo;&nbsp;découverte&nbsp;&raquo; de cette bonne pratique grâce aux dojos, j&rsquo;ai donc revisité mon <a href="http://bruno-orsier.developpez.com/tutoriels/TDD/pentaminos/index.php" target="_blank">tutoriel sur le développement dirigé par les tests (TDD)</a>, et essayé d&rsquo;améliorer les noms des tests. Le tableau ci-dessous présente les noms de tests du tutoriel, ainsi que le nouveau nom que je leur ai trouvé, parfois avec difficulté. </p>
<p>Pour ce tableau j&rsquo;ai suivi la convention utilisée par mes collègues, à savoir des groupes de mots séparés par des caractères &laquo;&nbsp;souligné&nbsp;&raquo;, comme dans <em>LeNombreDeSolutionsCalculees_DoitEtreEgal_AuNombreDeSolutionsTheoriques</em>. Mes collègues ne trouvent pas très lisibles d&rsquo;autres conventions comme tout séparer par des &laquo;&nbsp;souligné&nbsp;&raquo;, comme dans <em>le_nombre_de_solution_calculees_doit_etre_egal_au_nombre_de_solution_theoriques</em>, ou comme tout regrouper et &laquo;&nbsp;séparer&nbsp;&raquo; par des majuscules, comme dans <em>LeNombreDeSolutionsCalculeesDoitEtreEgalAuNombreDeSolutionsTheoriques</em>. </p>
<p>En ce qui me concerne j&rsquo;ai une préférence pour <em>le_nombre_de_solution_calculees_doit_etre_egal_au_nombre_de_solution_theoriques</em>, tout simplement parce qu&rsquo;il est facile d&rsquo;automatiser l&rsquo;obtention d&rsquo;un tel nom : il suffit d&rsquo;écrire une phrase en français, et une simple macro permet d&rsquo;obtenir un nom satisfaisant pour un compilateur (voir par exemple <a href="http://blog.jpboodhoo.com/MacroToAidBDDTestNamingStyle.aspx" target="_blank">Macro to aid BDD test naming style</a>). La troisième convention est également automatisable, mais moins lisible à mon goût.</p>
<p>Pour mieux comprendre les noms ci-dessous, il faut savoir que les 12 pentaminos sont représentés chacun par une lettre qui correspond à sa forme : X, L, V, I etc.</p>
<table cellspacing="0" cellpadding="2" width="856" border="1">
<tbody>
<tr>
<td valign="top" width="282">
<h1><strong>Avant</strong></h1>
</td>
<td valign="top" width="572">
<h1><strong>Après</strong></h1>
</td>
</tr>
<tr>
<td valign="top" width="282">NombreDeSolutions</td>
<td valign="top" width="572">LeNombreDeSolutionsCalculees_DoitEtreEgal_AuNombreDeSolutionsTheoriques</td>
</tr>
<tr>
<td valign="top" width="282">EchangeDeDimensions</td>
<td valign="top" width="572">LeNombreDeSolutions_DoitResterInvariant_ApresEchangeDeDimensions</td>
</tr>
<tr>
<td valign="top" width="282"> </td>
<td valign="top" width="572"> </td>
</tr>
<tr>
<td valign="top" width="282">TestTotalPentaminos</td>
<td valign="top" width="572">LaFabrique_DoitRetourner_ToutesLesVariantes</td>
</tr>
<tr>
<td valign="top" width="282">TestPositionDuPentominoX</td>
<td valign="top" width="572">LePentaminoX_DoitEtre_CorrectementPositionné_SurUneGrilleDe10x6</td>
</tr>
<tr>
<td valign="top" width="282"> </td>
<td valign="top" width="572"> </td>
</tr>
<tr>
<td valign="top" width="282">TestAjoutPentamino</td>
<td valign="top" width="572">LePentaminoI_DoitPouvoir_EtreAjouteEnPosition1</td>
</tr>
<tr>
<td valign="top" width="282">TestAjoutPentaminoSansRepetition</td>
<td valign="top" width="572">LePentaminoI_NeDoitPasPouvoir_EtreAjouteDeuxFoisEnPosition1</td>
</tr>
<tr>
<td valign="top" width="282">TestSolutionTrouveePlateauVide</td>
<td valign="top" width="572">PourUnPlateauVide_AucuneSolution_NeDoitEtreTrouvee</td>
</tr>
<tr>
<td valign="top" width="282">PlaceLibreSurPlateauVide</td>
<td valign="top" width="572">PourUnPlateauVide_LaPosition1_DoitEtreLibre</td>
</tr>
<tr>
<td valign="top" width="282">PlaceNonLibreXapresI</td>
<td valign="top" width="572">LaPosition1_NeDoitPlusEtreLibrePourX_ApresAjoutDeI</td>
</tr>
<tr>
<td valign="top" width="282">PlaceNonLibreEnFinDeLigne</td>
<td valign="top" width="572">LaPlace_NeDoitPasEtreLibre_EnFinDeLigne_PourI</td>
</tr>
<tr>
<td valign="top" width="282">PlaceNonLibrePourXen65</td>
<td valign="top" width="572">LaPlace_NeDoitPasEtreLibre_EnPosition65_PourX</td>
</tr>
<tr>
<td valign="top" width="282">PlaceNonLibrePourXen37</td>
<td valign="top" width="572">LaPlace_NeDoitPasEtreLibre_EnPosition37_PourX</td>
</tr>
<tr>
<td valign="top" width="282">PlaceLibrePourXen45</td>
<td valign="top" width="572">LaPlace_DoitEtreLibre_EnPosition45_PourX</td>
</tr>
<tr>
<td valign="top" width="282">ProchainePositionPlateauVide</td>
<td valign="top" width="572">PourUnPlateauVide_LaProchainePositionLibre_DoitEtre1</td>
</tr>
<tr>
<td valign="top" width="282">ProchainePositionPlateauApresI</td>
<td valign="top" width="572">ApresAjoutDeI_EnPosition1_LaProchainePositionLibre_DoitEtre6</td>
</tr>
<tr>
<td valign="top" width="282">DepassementLimites</td>
<td valign="top" width="572">PourUnPlateauVide_LePentaminoP_PeutEtreAjoute_EnPosition62</td>
</tr>
<tr>
<td valign="top" width="282">PlacementVenPosition1</td>
<td valign="top" width="572">ApresAjoutDeV_EnPosition1_LesLibertesDePlace_DoiventEtreJustes_EnDiversEndroits</td>
</tr>
<tr>
<td valign="top" width="282">PlacementVenPosition1viaLignes</td>
<td valign="top" width="572">ApresAjoutDeV_EnPosition1_LesLignes_DoiventContenir_Respectivement3_1_1_lettresV</td>
</tr>
<tr>
<td valign="top" width="282">PlacementLenPosition5viaLignes</td>
<td valign="top" width="572">ApresAjoutDeL_EnPosition5_LesLignes_DoiventContenir_Respectivement1_4_lettresL</td>
</tr>
<tr>
<td valign="top" width="282">AjouterPuisEnleverI</td>
<td valign="top" width="572">AjouterPuisEnleverI_DoitEtre_UneOperationInvariante_VerificationParAjout</td>
</tr>
<tr>
<td valign="top" width="282">PositionLibreApresAjouterPuisEnleverI</td>
<td valign="top" width="572">AjouterPuisEnleverI_DoitEtre_UneOperationInvariante_VerificationParPositionLibre</td>
</tr>
<tr>
<td valign="top" width="282"> </td>
<td valign="top" width="572"> </td>
</tr>
<tr>
<td valign="top" width="282">ConstructionSolutionComplete</td>
<td valign="top" width="572">AjouterDouzePentaminos_DansLeBonOrdre_DoitEtrePossible</td>
</tr>
<tr>
<td valign="top" width="282">SolutionTrouvee</td>
<td valign="top" width="572">AjouterDouzePentaminos_DansLeBonOrdre_DoitDonnerUneSolution</td>
</tr>
<tr>
<td valign="top" width="282">DescriptionLigne1</td>
<td valign="top" width="572">PourUneSolutionConnue_LaLigne1_DoitEtreJuste</td>
</tr>
<tr>
<td valign="top" width="282">DescriptionLigne2</td>
<td valign="top" width="572">PourUneSolutionConnue_LaLigne2_DoitEtreJuste</td>
</tr>
<tr>
<td valign="top" width="282">DescriptionLigne3</td>
<td valign="top" width="572">PourUneSolutionConnue_LaLigne3_DoitEtreJuste</td>
</tr>
<tr>
<td valign="top" width="282">DescriptionLigne4</td>
<td valign="top" width="572">PourUneSolutionConnue_LaLigne4_DoitEtreJuste</td>
</tr>
<tr>
<td valign="top" width="282">DescriptionLigne5</td>
<td valign="top" width="572">PourUneSolutionConnue_LaLigne5_DoitEtreJuste</td>
</tr>
<tr>
<td valign="top" width="282">DescriptionLigne6</td>
<td valign="top" width="572">PourUneSolutionConnue_LaLigne6_DoitEtreJuste</td>
</tr>
<tr>
<td valign="top" width="282"> </td>
<td valign="top" width="572"> </td>
</tr>
<tr>
<td valign="top" width="282">TestPosition27DansPremierQuadrant</td>
<td valign="top" width="572">LaPosition27_DoitEtre_DansLePremierQuadrant</td>
</tr>
<tr>
<td valign="top" width="282">TestPosition17DansPremierQuadrant</td>
<td valign="top" width="572">LaPosition17_DoitEtre_DansLePremierQuadrant</td>
</tr>
<tr>
<td valign="top" width="282"> </td>
<td valign="top" width="572"> </td>
</tr>
<tr>
<td valign="top" width="282">TestDescription</td>
<td valign="top" width="572">LaDescriptionDuProgramme_DoitEtre_NonVide</td>
</tr>
</tbody>
</table>
<p> </p>
<p>Obtenir de bons noms a été finalement assez difficile, et consommateur de temps. D&rsquo;ailleurs je ne suis pas forcément satisfait de tous. Il est probable que travailler en binôme sur la question permettrait d&rsquo;arriver à de meilleurs résultats. </p>
<p>J&rsquo;ai constaté aussi que la difficulté de trouver certains noms pouvait indiquer une &laquo;&nbsp;mauvaise odeur&nbsp;&raquo;. Par exemple pour <em>ApresAjoutDeV_EnPosition1_LesLibertesDePlace_DoiventEtreJustes_EnDiversEndroits</em>, la longueur et le caractère vague  du nom m&rsquo;indiquent que le test fait sans doute trop de choses, et qu&rsquo;il vaudrait mieux le couper en plusieurs petits tests.</p>
<p>Travailler sur les noms a été également révélateur de défauts potentiels de structuration de mes tests. Ainsi l&rsquo;une des classes comporte quatre méthodes dont les noms sont construits de manière similaire : <em>PourUnPlateauVide_LePentaminoP_PeutEtreAjoute_EnPosition62, PourUnPlateauVide_LaProchainePositionLibre_DoitEtre1, PourUnPlateauVide_LaPosition1_DoitEtreLibre, PourUnPlateauVide_AucuneSolution_NeDoitEtreTrouvee,</em> alors que les autres noms sont bien différents. Cela indique que ces quatre méthodes devraient certainement aller sur une autre classe de tests, ce qui permettrait de factoriser le contexte &laquo;&nbsp;Pour un plateau vide&nbsp;&raquo;. Les noms pourraient alors être simplifiés, ce qui augmenterait la lisibilité. </p>
<p>En conclusion, appliquer cette bonne pratique a été un exercice utile pour améliorer mon tutoriel dans une future version, et c&rsquo;est une pratique que je m&rsquo;efforcerai d&rsquo;appliquer au quotidien.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Tests unitaires et vérification d&#8217;invariants</title>
		<link>https://blog.developpez.com/bruno-orsier/p6708/tests-unitaires/tests_unitaires_et_verification_d_invari</link>
		<comments>https://blog.developpez.com/bruno-orsier/p6708/tests-unitaires/tests_unitaires_et_verification_d_invari#comments</comments>
		<pubDate>Tue, 04 Nov 2008 11:59:52 +0000</pubDate>
		<dc:creator><![CDATA[Bruno Orsier]]></dc:creator>
				<category><![CDATA[Tests unitaires]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Quand je passe en revue les tests unitaires écrits sur nos applications, je constate une énorme proportion de tests du type &#171;&#160;tel calcul doit donner précisément telle valeur&#160;&#187;. Ce type de test est bien sûr tout à fait nécessaire. Toutefois je constate que d&#8217;autres tests ne sont pas aussi souvent présents qu&#8217;ils pourraient l&#8217;être. Ce sont des tests qui exploitent les propriétés particulières des objets métiers, et ces tests sont très courts et très faciles [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Quand je passe en revue les tests unitaires écrits sur nos applications, je constate une énorme proportion de tests du type &laquo;&nbsp;tel calcul doit donner précisément telle valeur&nbsp;&raquo;. Ce type de test est bien sûr tout à fait nécessaire. Toutefois je constate que d&rsquo;autres tests ne sont pas aussi souvent présents qu&rsquo;ils pourraient l&rsquo;être. Ce sont des tests qui exploitent les propriétés particulières des objets métiers, et ces tests sont très courts et très faciles à mettre en oeuvre. Il serait donc dommage de s&rsquo;en priver. Mon dernier <a href="http://bruno-orsier.developpez.com/tutoriels/TDD/conservation/information/index.php">article</a> illustre ce type de tests, dont le point commun semble être un<strong> principe de vérification d&rsquo;invariances</strong>.</p>
<p>L&rsquo;article est illustré par des exemples en C#, exemples souvent tirés de cas réels dans des applications scientifiques.</p>
<p>N&rsquo;hésitez pas à utiliser ce billet pour poster des commentaires sur l&rsquo;article.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
