{"id":1991,"date":"2014-09-07T10:36:36","date_gmt":"2014-09-07T09:36:36","guid":{"rendered":"http:\/\/qualilogy.com\/fr\/?p=1991"},"modified":"2014-09-08T16:42:43","modified_gmt":"2014-09-08T15:42:43","slug":"application-legacy-refactoring-reengineering-5","status":"publish","type":"post","link":"http:\/\/qualilogy.com\/fr\/application-legacy-refactoring-reengineering-5\/","title":{"rendered":"Application Legacy \u2013 Refactoring ou reengineering? (V)"},"content":{"rendered":"<p><a href=\"http:\/\/500px.com\/Vicken\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"alignright size-full wp-image-1992\" src=\"http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/Qualilogy-Legacy-Characterization-Tests.jpg\" alt=\"Legacy ou Reengineering - Tests de caracterisation\" width=\"239\" height=\"360\" srcset=\"http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/Qualilogy-Legacy-Characterization-Tests.jpg 239w, http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/Qualilogy-Legacy-Characterization-Tests-199x300.jpg 199w\" sizes=\"(max-width: 239px) 100vw, 239px\" \/><\/a>Dans notre <a title=\"Application Legacy \u2013 Refactoring ou reengineering? (IV)\" href=\"http:\/\/qualilogy.com\/fr\/application-legacy-refactoring-reengineering-4\/\" target=\"_blank\">post pr\u00e9c\u00e9dent<\/a>, nous avons repris la d\u00e9finition de Michael Feathers (depuis son livre \u00ab\u00a0Working Effectively with Legacy Code\u00a0\u00bb) selon lequel l\u2019absence de tests unitaires est le facteur d\u00e9terminant d\u2019une application Legacy. Il propose le concept de test de caract\u00e9risation afin de comprendre le comportement de l\u2019application, c&rsquo;est-\u00e0-dire ce qu\u2019elle fait r\u00e9ellement, et non pas de chercher \u00e0 d\u00e9couvrir \u00e0 travers le code ce qu\u2019elle est cens\u00e9e faire.<\/p>\n<p>Mais qu\u2019en est-il lorsque notre application Legacy ne dispose pas d\u00e9j\u00e0 de tests unitaires ? La r\u00e9ponse \u00e0 l&rsquo;un de nos 3 sc\u00e9narios \u2013 plan de transfert de l\u2019application \u00e0 une autre \u00e9quipe \u2013 peut-elle passer par l\u2019\u00e9criture de tests ? Est-il possible de faciliter ce transfert de connaissances avec des tests de ce type ? <!--more--><\/p>\n<h2>Tests de caract\u00e9risation<\/h2>\n<p>Michael Feathers pr\u00e9conise dans son livre de r\u00e9aliser pour ce faire des tests destin\u00e9s, non pas seulement \u00e0 v\u00e9rifier que le code est correct, mais aussi et surtout \u00e0 caract\u00e9riser son comportement, c&rsquo;est-\u00e0-dire \u00e0 d\u00e9couvrir ce que fait r\u00e9ellement ce code. Ceci pr\u00e9sente donc le double avantage de :<\/p>\n<ul>\n<li>Permettre, sinon le transfert, du moins l\u2019acquisition de la connaissance applicative par une nouvelle \u00e9quipe.<\/li>\n<li>D\u00e9velopper des tests qui seront valides pour notre future op\u00e9ration de refactoring ou de re-engineering puisque le comportement de notre application doit rester constant suite \u00e0 cette op\u00e9ration.<\/li>\n<\/ul>\n<p>Michael Feathers recommande de proc\u00e9der de la mani\u00e8re suivante :<\/p>\n<ol>\n<li>Pour un bloc de code que l\u2019on souhaite tester \/ documenter, \u00e9crire un test dont on sait qu\u2019il \u00e9chouera.<\/li>\n<li>Lancer le test et noter la r\u00e9ponse attendue par le bloc de code, correspondant donc au comportement attendu.<\/li>\n<li>Modifier le test de mani\u00e8re \u00e0 refl\u00e9ter le comportement correct (donc un test qui retourne un r\u00e9sultat positif).<\/li>\n<li>R\u00e9p\u00e9tez autant de fois que souhait\u00e9 pour ce bloc de code.<\/li>\n<\/ol>\n<p><a href=\"http:\/\/www.objectmentor.com\/resources\/articles\/WorkingEffectivelyWithLegacyCode.pdf\" target=\"_blank\">L\u2019exemple<\/a> que donne Michael Feathers correspond \u00e0 du langage Java, et peut s\u2019av\u00e9rer diff\u00e9rent pour du code Legacy en C. Dans son impl\u00e9mentation tout au moins, mais cela d\u00e9pendra de toutes fa\u00e7ons de la mani\u00e8re dont vous souhaitez proc\u00e9der, notamment si vous utilisez un framework de tests, en fonction des possibilit\u00e9s et des fonctionnalit\u00e9s de ce dernier. Je ne vais pas m\u2019\u00e9tendre sur ce point : les d\u00e9veloppeurs C\/C++ en savent plus que moi sur ce sujet et comprendront ce dont il s\u2019agit.<\/p>\n<h2>Exemple de la fonction RTFOUT<\/h2>\n<p>Sur le fond, la m\u00e9thode pr\u00e9conis\u00e9e par Michael Feathers reste la m\u00eame, quelque soit la technologie (C, Java, etc) que vous souhaitez \u2018caract\u00e9riser\u2019 et le(s) outil(s) que vous allez employer. Voyons en un exemple avec notre application Word Opus 1.1a.<\/p>\n<p>Nous savons que <a title=\"Application Legacy en C \u2013 Refactoring ou r\u00e9ing\u00e9nierie ? (II)\" href=\"http:\/\/qualilogy.com\/fr\/application-legacy-c-refactoring-reingenierie-2\/\" target=\"_blank\">la fonction la plus complexe<\/a>, avec 355 points de CC (Complexit\u00e9 Cyclomatique) compte 2 063 LOCs (Line Of Code) !<br \/>\nUn coup d\u2019\u0153il dans le dashboard SonarQube m\u2019apprend que cette fonction se trouve dans un fichier du m\u00eame nom \u2018Opus\\RTFOUT.c\u2019, dont elle repr\u00e9sente la quasi-totalit\u00e9 du code (il existe une autre fonction comportant 2 points de CC), le tout avec 1 124 instructions et un taux de commentaires de 17.5%.<br \/>\n<a href=\"http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOut1.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2000\" src=\"http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOut1.jpg\" alt=\"La fonction RTFOut (Word) dans le dashboard SonarQube\" width=\"506\" height=\"142\" srcset=\"http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOut1.jpg 506w, http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOut1-300x84.jpg 300w\" sizes=\"(max-width: 506px) 100vw, 506px\" \/><\/a><br \/>\nLes 200 premi\u00e8res lignes constituent une accumulation d\u2019includes et de variables aux noms \u00e9sot\u00e9riques, et non document\u00e9es ou mal comment\u00e9es :<\/p>\n<p><a href=\"http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOut2.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2001\" src=\"http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOut2.jpg\" alt=\"La fonction RTFOUT (Word)\" width=\"546\" height=\"331\" srcset=\"http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOut2.jpg 546w, http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOut2-300x181.jpg 300w\" sizes=\"(max-width: 546px) 100vw, 546px\" \/><\/a>Inutile m\u00eame d\u2019essayer d\u2019y comprendre quoi que ce soit.<br \/>\nPar contre, apr\u00e8s quelques if .. else imbriqu\u00e9s, je tombe rapidement sur le switch suivant :<\/p>\n<p><a href=\"http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOutSwitchFF.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2002\" src=\"http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOutSwitchFF.jpg\" alt=\"Switch dans la fonction TRFOut\" width=\"501\" height=\"416\" srcset=\"http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOutSwitchFF.jpg 501w, http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOutSwitchFF-300x249.jpg 300w\" sizes=\"(max-width: 501px) 100vw, 501px\" \/><\/a>Je comprends alors rapidement que nous sommes en pr\u00e9sence de la fonction charg\u00e9e de produire le format RTF (Rich Text Format) du texte entr\u00e9 sous Word, et ce premier switch va sp\u00e9cifier le type de police \u2013 Modern, Roman, Swiss, etc. \u2013 utilis\u00e9 dans ce format. Et que la variable \u2018fmc\u2019 va g\u00e9rer ces valeurs.<\/p>\n<p>Un autre switch, assez long, concerne les propri\u00e9t\u00e9s du document, lorsqu\u2019elles existent : le titre, le sujet, l\u2019auteur, etc.,<br \/>\n<a href=\"http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOutSwitchP1.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2003\" src=\"http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOutSwitchP1.jpg\" alt=\"Switch fonction RTFOut\" width=\"321\" height=\"344\" srcset=\"http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOutSwitchP1.jpg 321w, http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOutSwitchP1-279x300.jpg 279w\" sizes=\"(max-width: 321px) 100vw, 321px\" \/><\/a>\u2026 la date de derni\u00e8re \u00e9dition du document, le nombre de pages, le nombre de mots ou de caract\u00e8res :<\/p>\n<p><a href=\"http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOutSwitchP2.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2004\" src=\"http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOutSwitchP2.jpg\" alt=\"Switch fonction RTFOut\" width=\"307\" height=\"232\" srcset=\"http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOutSwitchP2.jpg 307w, http:\/\/qualilogy.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/09\/WordRTFOutSwitchP2-300x226.jpg 300w\" sizes=\"(max-width: 307px) 100vw, 307px\" \/><\/a>Je constate donc que je peux d\u00e9j\u00e0 rep\u00e9rer tous les blocs de code suffisamment simples et lisibles pour d\u00e9velopper des tests de caract\u00e9risation, un pour chaque valeur possible rencontr\u00e9e dans une structure conditionnelle (end..if, switch) ou de boucle (qui comporte \u00e9galement une condition). En l\u2019esp\u00e8ce, nous pouvons \u00e9crire un ou plusieurs tests afin de v\u00e9rifier des valeurs existantes ou non.<\/p>\n<p>Par exemple, je testerai les diff\u00e9rents \u00e9tats possible de la variables \u2018fmc\u2019 avec les valeurs que nous pouvons d\u00e9couvrir dans le code : \u2018FF_ROMAN\u2019, \u2018FF_MODERN\u2019, \u2026 ou une valeur inexistante \u2018FF_WRONG\u2019 afin de voir comment r\u00e9agit l\u2019application.<\/p>\n<p>De m\u00eame pour la variable \u2018flt\u2019 qui g\u00e8re les propri\u00e9t\u00e9s du document : je peux tester toutes sortes de valeurs \u2018anormales\u2019, afin de voir encore une fois comment se comporte l\u2019application dans ce cas. Que se passe-t-il par exemple si j\u2019effectue un test :<\/p>\n<ul>\n<li>Avec un nombre de pages \u00e9gal \u00e0 999 999 ?<\/li>\n<li>Avec des caract\u00e8res sp\u00e9ciaux (@, #, !, \u00bf, \u2026) dans le nom de l\u2019auteur ?<\/li>\n<li>Avec des formats de date diff\u00e9rents pour la date de derni\u00e8re mise \u00e0 jour ?<\/li>\n<\/ul>\n<p>Bien s\u00fbr, certains blocs de code, comme la gestion en m\u00e9moire de la table des bookmarks dans un document Word, vont s\u2019av\u00e9rer trop complexes pour qu\u2019il soit possible de les comprendre et de les tester correctement sans aucune aide. Mais rappelons que l\u2019objectif premier n\u2019est pas de comprendre ce qu\u2019est cens\u00e9e faire l\u2019application \u00e0 travers son code, sinon de caract\u00e9riser son comportement.<\/p>\n<p>Je rencontre \u00e9galement tr\u00e8s rapidement des blocs de code qui sont r\u00e9p\u00e9t\u00e9s avant chaque boucle ou chaque switch. Je suppose qu\u2019il s\u2019agit de variables initialis\u00e9es avant chaque traitement et mise \u00e0 jour durant celui-ci. Je note de v\u00e9rifier dans la documentation (si elle existe) ou de demander \u00e0 l\u2019\u00e9quipe actuelle \u00e0 quoi correspondent ces variables. Si elles sont r\u00e9p\u00e9t\u00e9es si souvent, cela peut avoir un impact en termes de design lors d\u2019un refactoring ou d\u2019un re-engineering.<\/p>\n<p>NOTE : un des probl\u00e8mes avec le C est que certaines fonctions seront externalis\u00e9es dans des includes ou une macro. Comme le d\u00e9crit Michael Feathers dans son livre, il est tout \u00e0 fait possible de modifier le code de mani\u00e8re \u00e0 cr\u00e9er notre propre include avec un appel \u00e0 ces fonctions, afin de tester rapidement si celles-ci sont appel\u00e9es avec le bon nombre et le bon type de param\u00e8tres. N\u2019h\u00e9sitez pas \u00e0 vous r\u00e9f\u00e9rez \u00e0 son livre si vous avez des questions : je ne vais pas \u00e9voquer toutes celles-ci dans ce post.<br \/>\nAutre probl\u00e8me que j&rsquo;ai rencontr\u00e9 dans le code : beaucoup de directives de compilation #IFNDEF ou pour des plateformes diff\u00e9rentes (Mac). Donc \u00e0 prendre en compte au niveau du reengineering.<\/p>\n<h2>Synth\u00e8se<\/h2>\n<p>L\u2019avantage de la d\u00e9marche pr\u00e9conis\u00e9e par Michael Feathers est de nous int\u00e9resser non pas \u00e0 ce que l\u2019application est cens\u00e9e faire, \u00e0 travers son code, ce qui est toujours tr\u00e8s long, voire parfois impossible, mais \u00e0 ce que l\u2019application fait r\u00e9ellement. D\u2019autant que l\u2019application ne se comporte pas toujours comme elle est cens\u00e9e le faire.<\/p>\n<p>Il est possible de cr\u00e9er tr\u00e8s rapidement des tests sur des blocks de code impliquant des conditions, au sein de structures conditionnelles (if..else, switch) ou de boucle. N\u2019oublions pas que chaque \u2018path\u2019 ou \u2018chemin\u2019 dans ces structures correspond \u00e0 une r\u00e8gle de logique m\u00e9tier (ou technique) et donc doit normalement \u00eatre couverte par un test correspondant.<\/p>\n<p>Ce qui nous am\u00e8ne \u00e0 la question suivante : combien de tests de caract\u00e9risation sont n\u00e9cessaires afin d\u2019assurer un transfert de connaissances (un de nos 3 sc\u00e9narios) ? Quelle couverture de code assurer par ces tests avant de commencer un refactoring ou un re-engineering ? Peut-on estimer l\u2019effort de tests que cela repr\u00e9sente ? C\u2019est ce que nous verrons dans notre prochain post.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dans notre post pr\u00e9c\u00e9dent, nous avons repris la d\u00e9finition de Michael Feathers (depuis son livre \u00ab\u00a0Working Effectively with Legacy Code\u00a0\u00bb) selon lequel l\u2019absence de tests unitaires est le facteur d\u00e9terminant d\u2019une application Legacy. Il propose le concept de test de caract\u00e9risation afin de comprendre le comportement de l\u2019application, c&rsquo;est-\u00e0-dire ce qu\u2019elle fait r\u00e9ellement, et non [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-1991","post","type-post","status-publish","format-standard","hentry","category-qualite-des-applications"],"_links":{"self":[{"href":"http:\/\/qualilogy.com\/fr\/wp-json\/wp\/v2\/posts\/1991"}],"collection":[{"href":"http:\/\/qualilogy.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/qualilogy.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/qualilogy.com\/fr\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"http:\/\/qualilogy.com\/fr\/wp-json\/wp\/v2\/comments?post=1991"}],"version-history":[{"count":23,"href":"http:\/\/qualilogy.com\/fr\/wp-json\/wp\/v2\/posts\/1991\/revisions"}],"predecessor-version":[{"id":2022,"href":"http:\/\/qualilogy.com\/fr\/wp-json\/wp\/v2\/posts\/1991\/revisions\/2022"}],"wp:attachment":[{"href":"http:\/\/qualilogy.com\/fr\/wp-json\/wp\/v2\/media?parent=1991"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/qualilogy.com\/fr\/wp-json\/wp\/v2\/categories?post=1991"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/qualilogy.com\/fr\/wp-json\/wp\/v2\/tags?post=1991"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}