Jekyll2021-11-10T15:38:05+00:00https://sunye-g.univ-nantes.io/Gerson SunyéUniversité de NantesMaven, la suite2021-11-04T08:00:00+00:002021-11-04T08:00:00+00:00https://sunye-g.univ-nantes.io/java/maven/2021/11/04/maven-suite<h2 id="introduction">Introduction</h2>
<p>Maintenant que vous avez créé votre premier projet Maven, vous vous voyez confronté à un scénario un peu différent.
Deux de vos amis ont développé une application capable de gérer des agendas et de les synchroniser avec <em>Google Calendar</em>.
Vous trouvez leur application géniale, mais vous ne savez pas comment l’intégrer à votre projet.
En effet, bien que sympathiques, vos amis sont fainéants: ils ne vous ont laissé que le <a href="https://gitlab.univ-nantes.fr/sunye-g/dates">code source</a> et
un fichier <code class="highlighter-rouge">README</code> succinct.</p>
<p>Deux options se présentent à vous: (i) copier leur code à votre projet ou (ii) le gérer comme un projet indépendant et utiliser Maven pour le transformer en un artéfact réutilisable.
Comme vous faites souvent de bons choix (et accessoirement, vous avez remarqué que ceci est un tutoriel sur Maven et non sur la copie de fichiers),
vous avez opté pour la deuxième option.
Vous allez élitiser <sup id="fnref:1"><a href="#fn:1" class="footnote">1</a></sup> leur projet.</p>
<p>Avant de commencer, créez une copie locale du projet:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://gitlab.univ-nantes.fr/sunye-g/dates.git
<span class="nb">cd </span>dates
</code></pre></div></div>
<h2 id="configuration-du-projet">Configuration du projet</h2>
<p>Notre première étape sera de créer le modèle objet du projet, le fichier <code class="highlighter-rouge">pom.xml</code>.
Cette fois, nous n’allons pas utiliser un archétype pour créer le squelette du projet, car le projet existe déjà,
nous allons tout simplement créer le fichier manuellement.
Bien évidemment, vous pouvez copier le modèle d’un autre projet et modifier ensuite les identifiants de groupe et d’artéfact.</p>
<p>Créez un fichier appelé <code class="highlighter-rouge">pom.xml</code> à la racine du projet <strong>Dates</strong> et ajoutez-y les balises XML suivantes:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?xml version="1.0" encoding="UTF-8"?></span>
<span class="nt"><project</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span class="na">xmlns=</span><span class="s">"http://maven.apache.org/POM/4.0.0"</span>
<span class="na">xsi:schemaLocation=</span><span class="s">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span><span class="nt">></span>
<span class="nt"><modelVersion></span>4.0.0<span class="nt"></modelVersion></span>
<span class="nt"><groupId></span>fr.univnantes.student<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>dates<span class="nt"></artifactId></span>
<span class="nt"><version></span>1.0-SNAPSHOT<span class="nt"></version></span>
<span class="nt"><properties></span>
<span class="nt"><maven.compiler.source></span>11<span class="nt"></maven.compiler.source></span>
<span class="nt"><maven.compiler.target></span>11<span class="nt"></maven.compiler.target></span>
<span class="nt"></properties></span>
<span class="nt"></project></span>
</code></pre></div></div>
<p>Vous pouvez désormais compiler le projet avec Maven:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mvn compile
</code></pre></div></div>
<p>Comme vous pouvez constater, la compilation est un succès.
Cela n’est pas dû à la qualité du code de vos amis, mais tout simplement parce que le compilateur Java n’a pas trouvé de code à compiler.</p>
<h2 id="réorganisation-des-dossiers">Réorganisation des dossiers</h2>
<p>Il ne s’agit pas d’une erreur, dans le cadre d’un projet Maven, le compilateur Java cherche le code source dans le dossier <code class="highlighter-rouge">src/main/java</code>, exclusivement.
Il est bien sûr possible de dire à Maven de chercher le code source ailleurs, mais ce choix va à l’encontre d’un des principes de base de Maven:
<strong>la convention plutôt que la configuration.</strong></p>
<p>Nous allons donc respecter ce principe et déplacer le code source à sa place appropriée.
Utilisez les commandes <em>shell</em> suivantes pour réorganiser le projet:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir <span class="nt">-p</span> src/main/java
mv src/univ src/main/java
</code></pre></div></div>
<p>Compilez à nouveau le projet avec Maven:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mvn compile
</code></pre></div></div>
<p>Cette fois, le compilateur trouve plusieurs erreurs, ce qui est normal.
Il a trouvé le code source, mais n’arrive pas à trouver les artéfacts logiciels utilisés par ce code.</p>
<h2 id="configuration-des-dépendances">Configuration des dépendances</h2>
<p>Le fichier <code class="highlighter-rouge">README.adoc</code> nous donne une piste importante: le code source utilise les artéfacts <strong>MiG Layout</strong> et <strong>Google Data</strong>.
La bonne nouvelle c’est que ces deux artéfacts sont disponibles sur Maven Central.
Cependant, pour les retrouver Maven aura besoin de 2 choses: leurs identifiants (groupe et artéfact id) et leurs versions.
Utilisez le site <a href="https://mvnrepository.com">MVNRepository</a> pour retrouver les dépendances et ajoutez-les au fichier <code class="highlighter-rouge">pom.xml</code>.</p>
<p><strong>ASTUCE</strong>: Pour gagner du temps, utilisez les mots-clés <code class="highlighter-rouge">com.google.gdata</code> et <code class="highlighter-rouge">miglayout</code>.</p>
<p>A la fin, vous devez avoir une balise appelée <code class="highlighter-rouge"><dependencies></code>, qui a comme mère directe la balise <code class="highlighter-rouge"><project></code> et comme filles, deux balises <code class="highlighter-rouge"><dependency></code>:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><project></span>
<span class="c"><!-- ... --></span>
<span class="nt"><dependencies></span>
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>com.google.gdata<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>core<span class="nt"></artifactId></span>
<span class="nt"><version></span>1.47.1<span class="nt"></version></span>
<span class="nt"></dependency></span>
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>com.miglayout<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>miglayout<span class="nt"></artifactId></span>
<span class="nt"><version></span>3.7.4<span class="nt"></version></span>
<span class="nt"></dependency></span>
<span class="nt"></dependencies></span>
<span class="c"><!-- ... --></span>
<span class="nt"></project></span>
</code></pre></div></div>
<p>Recompilez le projet à l’aide de Maven avec <code class="highlighter-rouge">mvn compile</code>.
Cette fois, la compilation réussit avec quelques avertissements: le code utilise des méthodes qui ont été dépréciées.
Ces avertissements sont importants, car ils aideront les développeurs à mettre à jour le code pour garder la compatibilité avec les versions futures des artéfacts utilisés.
Nous allons exploiter cette information plus tard.</p>
<h2 id="documentation-du-projet">Documentation du projet</h2>
<p>Maintenant que le projet compile (presque) correctement, nous allons générer un site web contenant la documentation technique du projet.
Par défaut, Maven produit un rapport assez riche, que nous allons améliorer par la suite.
Nous allons commencer par configurer le <em>plugin</em> responsable de la phase <code class="highlighter-rouge">site</code> de la construction.</p>
<p>Ajoutez la balise suivante au fichier <code class="highlighter-rouge">pom.xml</code>:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><project></span>
<span class="c"><!-- (...) --></span>
<span class="nt"><build></span>
<span class="nt"><plugins></span>
<span class="nt"><plugin></span>
<span class="nt"><groupId></span>org.apache.maven.plugins<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>maven-site-plugin<span class="nt"></artifactId></span>
<span class="nt"><version></span>3.9.1<span class="nt"></version></span>
<span class="nt"></plugin></span>
<span class="nt"></plugins></span>
<span class="nt"></build></span>
<span class="nt"></project></span>
</code></pre></div></div>
<p>En réalité, on ne configure pas vraiment le <em>plug-in</em> <code class="highlighter-rouge">maven-site-plugin</code> ici, on se contente de préciser à Maven qu’il doit utiliser une version récente du <em>plug-in</em>:
en occurrence, la <code class="highlighter-rouge">3.9.1</code>.
Cette précision est nécessaire, car elle empêche Maven d’utiliser une version plus ancienne de ce <em>plug-in</em>, qui peut s’avérer avec le JDK utilisé.
Exécutez maintenant la phase <code class="highlighter-rouge">site</code> grâce à la commande suivante:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mvn site
</code></pre></div></div>
<p>Le <em>plug-in</em> génère les pages HTML dans le dossier <code class="highlighter-rouge">target/site/</code>.
Utilisez un navigateur pour ouvrir le fichier <code class="highlighter-rouge">target/site/index.html</code> et découvrir le rapport généré.
Parmi d’autres informations, vous trouverez les <em>plug-ins</em> utilisés par Maven, ainsi que les dépendances (directes et transitives) du projet.</p>
<h2 id="génération-de-la-javadoc">Génération de la Javadoc</h2>
<p>Vous avez probablement remarqué qu’à la racine du projet, il y a un dossier appelé <code class="highlighter-rouge">javadoc</code> qui contient la Javadoc générée par vos amis.
Ils ont pensé que c’était une bonne idée d’ajouter <strong>tous</strong> les fichiers générés au gestionnaire de versions, plutôt qu’un simple script capable de les générer.
Ce n’est pas le cas.
L’idée est mauvaise, mais vous pouvez les remercier: comme vous n’aurez pas le temps de faire toutes les erreurs du monde, vous pouvez apprendre des erreurs des autres.
Ou, pour citer Confucius:</p>
<blockquote>
<p>L’homme sage apprend de ses erreurs, l’homme plus sage apprend des erreurs des autres.</p>
<p><cite>Confucius</cite></p>
</blockquote>
<p>Vous avez compris, vous pouvez utiliser Maven pour générer automatiquement la Javadoc pendant le processus de <em>build</em>.
En effet, Maven est capable d’intégrer d’autres rapports au site généré et c’est ce que nous allons faire avec la Javadoc.</p>
<p>Ajoutez les balises suivante au fichier <code class="highlighter-rouge">pom.xml</code>:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><project></span>
<span class="c"><!-- (...) --></span>
<span class="nt"><reporting></span>
<span class="nt"><plugins></span>
<span class="nt"><plugin></span>
<span class="nt"><groupId></span>org.apache.maven.plugins<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>maven-javadoc-plugin<span class="nt"></artifactId></span>
<span class="nt"><version></span>3.3.1<span class="nt"></version></span>
<span class="nt"><configuration></span>
<span class="nt"><additionalJOption></span>-Xdoclint:none<span class="nt"></additionalJOption></span>
<span class="nt"></configuration></span>
<span class="nt"></plugin></span>
<span class="nt"></plugins></span>
<span class="nt"></reporting></span>
<span class="nt"></project></span>
</code></pre></div></div>
<p>La balise <code class="highlighter-rouge">reporting</code> nous permet de configurer les rapports qu’apparaitront dans le site web généré et
plus précisément dans la section <strong>Project Reports</strong>.
Le <em>plug-in</em> <code class="highlighter-rouge">maven-javadoc-plugin</code> permet plusieurs <a href="https://maven.apache.org/plugins/maven-javadoc-plugin/javadoc-mojo.html">options</a>.
Ici, on lui dit de ne pas arrêter la génération lorsqu’il rencontre des erreurs de syntaxe dans les commentaires.
Bien évidemment, il n’est pas nécessaire de dire que vos amis apprécient particulièrement ce type d’erreur.</p>
<p>Exécutez à nouveau la commande <code class="highlighter-rouge">mvn site</code> et rechargez la page <code class="highlighter-rouge">index.html</code>.
Le site généré contient désormais la Javadoc.
Remarquez que la Javadoc générée contient des liens hypertexte vers les classes de la JDK, comme <code class="highlighter-rouge">Object</code> ou <code class="highlighter-rouge">ActionListener</code>.</p>
<h2 id="Évaluation-de-la-qualité-du-coude-source">Évaluation de la qualité du coude source</h2>
<p>La Javadoc est certes une bonne source d’informations pour ceux qui souhaitent utiliser le code fait par vos amis,
mais ne donne aucune information sur la qualité de ce code.
En effet, toujours fainéants, vos amis n’ont écrit aucun test, ni unitaire, ni d’intégration.
Vous ne savez ni si le code est fiable ni s’il est maintenable.</p>
<p>Heureusement pour vous, plusieurs outils d’analyse statique de code Java existent et pourront vous aider à estimer la qualité du code source et à l’améliorer.
Par exemple:
<a href="https://pmd.github.io">PMD</a>,
<a href="https://checkstyle.sourceforge.io">Checkstyle</a>,
<a href="https://spotbugs.github.io">Spotbugs</a>,
<a href="http://errorprone.info">Error Prone</a>,
<a href="https://github.com/reactor/BlockHound">BlockHound</a> et quelques autres.</p>
<p>Ces outils peuvent s’intégrer aux rapports produits par Maven, grâce à des <em>plug-ins</em> spécifiques.
Pour évaluer le code de vos amis, nous allons en utiliser deux, qui permettent à Maven d’utiliser PMD et Spotbugs.
Ajoutez les balises suivantes à l’intérieur de la balise <code class="highlighter-rouge"><reporting></code> de votre fichier <code class="highlighter-rouge">pom.xml</code>:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><reporting></span>
<span class="nt"><plugins></span>
<span class="c"><!-- (...) --></span>
<span class="nt"><plugin></span>
<span class="nt"><groupId></span>com.github.spotbugs<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>spotbugs-maven-plugin<span class="nt"></artifactId></span>
<span class="nt"><version></span>4.4.2.2<span class="nt"></version></span>
<span class="nt"></plugin></span>
<span class="nt"><plugin></span>
<span class="nt"><groupId></span>org.apache.maven.plugins<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>maven-pmd-plugin<span class="nt"></artifactId></span>
<span class="nt"><version></span>3.15.0<span class="nt"></version></span>
<span class="nt"></plugin></span>
<span class="nt"></plugins></span>
<span class="nt"></reporting></span>
</code></pre></div></div>
<p>Exécutez à nouveau la commande <code class="highlighter-rouge">mvn site</code> et rechargez la page <code class="highlighter-rouge">index.html</code>.
Trois nouveaux rapports s’affichent à côté de celui de la Javadoc: SpotBugs, PMD et CPD.
Ce dernier, <em>Copy Paste Detector</em> affiche les morceaux de code dupliqués du projet.</p>
<p>Comme vous pouvez constater, le format des rapports est similaire, il s’agit d’un tableau contenant le nom de la règle (ou <em>bug</em> dans SpotBugs),
la classe et les lignes de code où la règle n’a pas été respectée.
Un inconvénient de ces rapports c’est qu’il n’est pas possible de les utiliser pour accéder directement au code source.</p>
<p>Un autre <em>plug-in</em> de Maven peut résoudre cet inconvénient: <a href="https://maven.apache.org/jxr/maven-jxr-plugin/">JXR</a>.
En effet, JXR permet la création de références croisées entre les rapports et le code source.
Son utilisation se fait en deux étapes: (i) ajout du <em>plug-in</em> à la génération du site et (ii) configuration des autres <em>plug-ins</em>.
Après ces modifications, la balise <code class="highlighter-rouge"><reporting></code> doit ressembler à:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><reporting></span>
<span class="nt"><plugins></span>
<span class="nt"><plugin></span>
<span class="nt"><groupId></span>org.apache.maven.plugins<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>maven-javadoc-plugin<span class="nt"></artifactId></span>
<span class="nt"><version></span>3.3.1<span class="nt"></version></span>
<span class="nt"><configuration></span>
<span class="nt"><additionalJOption></span>-Xdoclint:none<span class="nt"></additionalJOption></span>
<span class="nt"></configuration></span>
<span class="nt"></plugin></span>
<span class="nt"><plugin></span>
<span class="nt"><groupId></span>org.apache.maven.plugins<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>maven-jxr-plugin<span class="nt"></artifactId></span>
<span class="nt"><version></span>3.1.1<span class="nt"></version></span>
<span class="nt"></plugin></span>
<span class="nt"><plugin></span>
<span class="nt"><groupId></span>com.github.spotbugs<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>spotbugs-maven-plugin<span class="nt"></artifactId></span>
<span class="nt"><version></span>4.4.2.2<span class="nt"></version></span>
<span class="nt"><configuration></span>
<span class="nt"><linkXref></span>true<span class="nt"></linkXref></span>
<span class="nt"></configuration></span>
<span class="nt"></plugin></span>
<span class="nt"><plugin></span>
<span class="nt"><groupId></span>org.apache.maven.plugins<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>maven-pmd-plugin<span class="nt"></artifactId></span>
<span class="nt"><version></span>3.15.0<span class="nt"></version></span>
<span class="nt"><configuration></span>
<span class="nt"><linkXref></span>true<span class="nt"></linkXref></span>
<span class="nt"></configuration></span>
<span class="nt"></plugin></span>
<span class="nt"></plugins></span>
<span class="nt"></reporting></span>
</code></pre></div></div>
<h2 id="conclusion">Conclusion</h2>
<p>Maven n’est pas seulement utile lors de la création d’un nouveau projet, il l’est aussi pour les projets existants.
L’adoption des conventions Maven entraine des efforts supplémentaires, mais qui ne présente que des avantages.
En effet, elles ne sont pas exclusives aux projets Maven, elles sont aussi respectées par d’autres outils de construction,
comme <a href="https://gradle.org">Gradle</a> ou <a href="https://bazel.build">Bazel</a>.
Respecter ces conventions c’est rendre le code plus accessible aux autres développeurs.</p>
<p>Les outils d’analyse statique de code fournissent des informations très intéressantes sur le code source.
Mais ces informations ne doivent pas être considérées individuellement, mais dans leur globalité et dans le contexte d’un projet.
Ces outils sont personnalisables et peuvent s’adapter aux règles de chaque projet.</p>
<h2 id="notes">Notes</h2>
<div class="footnotes">
<ol>
<li id="fn:1">
<p>Le verbe “maveniser” n’existant pas et “Maven” signifiant “expert” ou “connaisseur”, je me permets cette localisation. <a href="#fnref:1" class="reversefootnote">↩</a></p>
</li>
</ol>
</div>IntroductionPremiers pas avec Maven2018-01-14T10:00:00+00:002018-01-14T10:00:00+00:00https://sunye-g.univ-nantes.io/java/maven/2018/01/14/maven<h2 id="introduction">Introduction</h2>
<p>Après avoir développé avec succès une Calculatrice en Java et vous souhaitez partager votre succès rayonnant avec tout le monde.
Et vous ne souhaitez pas vous arrêter là, vous souhaitez aussi que d’autres développeurs participent à votre projet et contribuent à son amélioration.</p>
<p>Vous avez de la chance, la fondation Apache a créé <a href="https://maven.apache.org">Maven</a>, un outil de gestion de la construction de logiciels.
Maven vous aidera à compiler et tester votre logiciel, mais aussi à le distribuer et à le rendre simple à comprendre par d’autres développeurs.</p>
<p><em>Note:</em> si vous utilisez les machines de l’Université de Nantes, vous devez configurer votre environnement avant d’utiliser Maven:</p>
<ul>
<li><a href="#fst">Configuration des machines à la Faculté des Sciences.</a></li>
<li><a href="#iut">Configuration des machines à l’IUT de Nantes.</a></li>
</ul>
<h2 id="convention-avant-la-configuration">Convention avant la configuration</h2>
<p>L’organisation d’un projet Maven respecte toujours la même convention: l’emplacement du code source, des tests, des resources, etc. est toujours le même.
Cette convention a deux conséquences:</p>
<ol>
<li>La prise en main d’un nouveau projet est plus simple: le nouveau développeur connaît d’emblée la structure du projet et ne perdra pas de temps à chercher l’emplacement des fichiers sources, des tests, etc.</li>
<li>Il n’est pas nécessaire de configurer Maven avant de l’utiliser. Il trouvera tout seul le code source et les tests unitaires de votre projet.</li>
</ol>
<p>Les conventions proposées par Maven sont très complètes. En voici quelques exemples:</p>
<table>
<thead>
<tr>
<th>Dossier</th>
<th>Contenu</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="highlighter-rouge">src/main/java</code></td>
<td>Fichiers source Java</td>
</tr>
<tr>
<td><code class="highlighter-rouge">src/test/java</code></td>
<td>Tests unitaires JUnit ou TestNG.</td>
</tr>
<tr>
<td><code class="highlighter-rouge">src/test/resources</code></td>
<td>Resources utilisés pendant les tests.</td>
</tr>
<tr>
<td><code class="highlighter-rouge">target</code></td>
<td>Fichiers générés pendant la construction du logiciel.</td>
</tr>
</tbody>
</table>
<h2 id="identification-et-création-dun-projet">Identification et création d’un projet</h2>
<p>Tout projet Maven possède un identifiant unique, composé de trois éléments: un identifiant de groupe, un identifiant de l’artefact et sa version.
Cet identifiant permet de créer des dépendances entre projets, qui sont gérées par Maven.
Par exemple, si votre projet utilise JUnit, vous n’avez pas besoin de télécharger une archive Java et de l’ajouter au projet, mais simplement de déclarer que votre projet a besoin de JUnit, identifié par le groupe “junit”, l’artefact “junit” et la version 4.12.</p>
<p>Ainsi, avant de créer votre projet, vous devez l’identifier.
L’identifiant est souvent celui de l’organisation à laquelle vous appartenez.
Par exemple, <code class="highlighter-rouge">fr.unantes</code>. Ensuite, l’identifiant de l’artefact est son nom, par exemple, <code class="highlighter-rouge">calculatrice</code>.</p>
<p>Une fois que votre projet est identifié, vous pouvez le créer par la commande Unix suivante:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code> mvn archetype:generate <span class="nt">-DarchetypeVersion</span><span class="o">=</span>1.4 <span class="nt">-DgroupId</span><span class="o">=</span>fr.unantes <span class="nt">-DartifactId</span><span class="o">=</span>calculatrice <span class="nt">-Dversion</span><span class="o">=</span>1.0
</code></pre></div></div>
<p>Cette commande créera un dossier appelé <code class="highlighter-rouge">calculatrice</code>, contenant différents dossiers vides et un fichier XML de configuration et d’information du projet, nommé <code class="highlighter-rouge">pom.xml</code>, appelé couramment un fichier POM.
Notez que cette commande spécifie aussi un «archétype», c’est à dire, la structure de projet à être construite.
L’archétype <code class="highlighter-rouge">generate</code> est un des plus simple et vous posera quelques questions durant la création.
Le contenu de ce fichier doit ressembler à ceci:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><project</span> <span class="na">xmlns=</span><span class="s">"http://maven.apache.org/POM/4.0.0"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span class="na">xsi:schemaLocation=</span><span class="s">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span><span class="nt">></span>
<span class="nt"><modelVersion></span>4.0.0<span class="nt"></modelVersion></span>
<span class="nt"><groupId></span>fr.unantes<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>calculatrice<span class="nt"></artifactId></span>
<span class="nt"><version></span>1.0<span class="nt"></version></span>
<span class="nt"><packaging></span>jar<span class="nt"></packaging></span>
<span class="nt"><name></span>calculatrice<span class="nt"></name></span>
<span class="nt"><url></span>http://maven.apache.org<span class="nt"></url></span>
<span class="nt"><properties></span>
<span class="nt"><project.build.sourceEncoding></span>UTF-8<span class="nt"></project.build.sourceEncoding></span>
<span class="nt"></properties></span>
<span class="nt"><dependencies></span>
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>junit<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>junit<span class="nt"></artifactId></span>
<span class="nt"><version></span>3.8.1<span class="nt"></version></span>
<span class="nt"><scope></span>test<span class="nt"></scope></span>
<span class="nt"></dependency></span>
<span class="nt"></dependencies></span>
<span class="nt"></project></span>
</code></pre></div></div>
<p>Comme vous pouvez constater, ce fichier contient l’identifiant du projet, une propriété (le type d’encodage) et une seule dépendance, vers la version 3.8.1 de JUnit.</p>
<p>Il existe d’autres moyens de créer un projet Maven, par exemple, directement dans Netbeans ou dans IntelliJ IDEA.
Il existe aussi d’autres archétypes, proposés par la fondation Apache ou par d’autres contributeurs.</p>
<p>Vous pouvez aussi modifier un projet existant et le transformer en projet Maven.
Pour cela, il suffit de créer ou copier un fichier POM et de respecter la structure de dossiers de Maven.</p>
<h2 id="dépendances">Dépendances</h2>
<p>Comme expliqué dans l’introduction, un projet Maven ne contient pas les archives Java des artéfacts qu’il utilise.
Par exemple, la dépendance à Junit 3.8.1, ajouté par défaut dans le fichier POM, dit à Maven de télécharger l’archive dans un référentiel local.
Plus précisément, dans le dossier <code class="highlighter-rouge">~/.m2/repository/junit/junit/3.8.1/</code>.
Cet artéfact n’est pas spécifique à votre projet, il peut être utilisé par d’autres projets Maven.
Si vous souhaitez utiliser une version plus récente de JUnit, il suffit de mettre à jour cette dépendance:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><dependency></span>
<span class="nt"><groupId></span>junit<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>junit<span class="nt"></artifactId></span>
<span class="nt"><version></span>4.12<span class="nt"></version></span>
<span class="nt"><scope></span>test<span class="nt"></scope></span>
<span class="nt"></dependency></span>
</code></pre></div></div>
<p>Maven se chargera de télécharger cette archive lorsqu’elle est nécessaire.
Sauf si elle a déjà été téléchargée. En fait, avant de télécharger une dépendance, Maven vérifie si elle n’est pas présente dans le référentiel local et si c’est le cas, il ne la téléchargera pas une deuxième fois.</p>
<p>Notez que la dépendance spécifie aussi la portée (scope) de l’artéfact.
Dans ce cas précis, il n’est utilisé que pendant les tests unitaires.
En termes Java, il n’est pas ajouté à la variable <code class="highlighter-rouge">CLASSPATH</code> pendant la compilation, mais le sera pendant les tests unitaires.</p>
<p>Si vous souhaitez utiliser d’autres artéfacts, il suffit de les ajouter au fichier POM.
Si par exemple, si vous souhaitez utiliser les annotation proposées par la <a href="http://www.oracle.com/technetwork/articles/java/ma14-architect-annotations-2177655.html">JSR 308</a>, il suffit de chercher son identifiant (utilisez par exemple le site <a href="https://mvnrepository.com/">MVNRepository</a>) et de l’ajouter au fichier POM:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><dependency></span>
<span class="nt"><groupId></span>net.java.loci<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>jsr308-all<span class="nt"></artifactId></span>
<span class="nt"><version></span>1.1.2<span class="nt"></version></span>
<span class="nt"></dependency></span>
</code></pre></div></div>
<h2 id="construction">Construction</h2>
<p>Comme dans tout outil de construction depuis plus de 40 ans (Make a été créé en 1977), la construction suit une séquence spécifique de phases. Par exemple, pour créer une archive Java, vous devez d’abord tester votre code.
Et pour tester le code, vous devez d’abord le compiler.</p>
<p>Maven n’échappe pas à la règle, la construction suit une séquence précise de phases, appelées <a href="https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html">cycle de vie</a>.
Au début, vous devez connaître 3 phases:</p>
<table>
<thead>
<tr>
<th>Phase</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="highlighter-rouge">compile</code></td>
<td>Compile le code Java, après avoir téléchargé les dépendances nécessaires.</td>
</tr>
<tr>
<td><code class="highlighter-rouge">test</code></td>
<td>Exécute les tests unitaires du projet, après l’avoir compilé.</td>
</tr>
<tr>
<td><code class="highlighter-rouge">package</code></td>
<td>Crée une archive Java du projet, après l’avoir testé.</td>
</tr>
</tbody>
</table>
<p>D’autres phases sont disponibles, par exemple pour nettoyer le dossier <code class="highlighter-rouge">target</code> ou générer le site web du projet.</p>
<h2 id="configuration-des-plugins">Configuration des plugins</h2>
<p>Chaque phase de construction (compilation, test, etc.) est associée à un plugin.
Dans la plupart des cas, vous n’avez pas besoin de les configurer, les valeurs par défaut sont suffisantes et marchent dans la plupart des cas.
Dans les autres cas, il est possible de configurer précisément chaque plugin à l’intérieur du fichier POM.
Par exemple, si vous souhaitez que votre code soit compilé par la version 9 de Java (en termes Java, <code class="highlighter-rouge">javac -source 1.9 -target 1.9</code>), vous pouvez ajouter les balises suivantes au fichier POM:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><project></span>
[...]
<span class="nt"><build></span>
[...]
<span class="nt"><plugins></span>
<span class="nt"><plugin></span>
<span class="nt"><groupId></span>org.apache.maven.plugins<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>maven-compiler-plugin<span class="nt"></artifactId></span>
<span class="nt"><version></span>3.7.0<span class="nt"></version></span>
<span class="nt"><configuration></span>
<span class="nt"><source></span>1.9<span class="nt"></source></span>
<span class="nt"><target></span>1.9<span class="nt"></target></span>
<span class="nt"></configuration></span>
<span class="nt"></plugin></span>
<span class="nt"></plugins></span>
[...]
<span class="nt"></build></span>
[...]
<span class="nt"></project></span>
</code></pre></div></div>
<h2 id="conclusion">Conclusion</h2>
<p>Né au sein du projet Jakarta de la fondation Apache, Maven est devenu un outil incontournable de construction automatique de projets Java, créant les bases de l’intégration continue.</p>
<p>Actuellement, Maven a un concurrent de poids, <a href="https://gradle.org">Gradle</a>, dont le fichier de configuration utilise un format plus lisible que le XML des fichiers POM.
Si Gradle n’est pas encore aussi populaire que Maven, il a le potentiel de le devenir.</p>
<p>La bonne nouvelle c’est que Gradle respecte les conventions établies par Maven et sait utiliser les référentiels local et distant des projets Maven.
On peut passer d’un projet Maven à un projet Gradle (et vice-versa) sans beaucoup d’effort.</p>
<h2 id="maven-à-la-faculté-des-sciences"><a name="fst"></a>Maven à la Faculté des Sciences</h2>
<p>La configuration du pare-feu de réseau de la Faculté des Sciences pose quelques problèmes au fonctionnement de Maven.
Le premier c’est que tout accès au réseau doit passer par le serveur proxy et Maven ne le sait pas.
Pour le configurer, vous devez créer un fichier appelé <code class="highlighter-rouge">settings.xml</code> dans le répertoire <code class="highlighter-rouge">~/.m2</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>touch ~/.m2/settings.xml
vim ~/.m2/settings.xml
</code></pre></div></div>
<p>Ajoutez ensuite les lignes suivantes:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><settings></span>
<span class="nt"><proxies></span>
<span class="nt"><proxy></span>
<span class="nt"><active></span>true<span class="nt"></active></span>
<span class="nt"><protocol></span>http<span class="nt"></protocol></span>
<span class="nt"><host></span>proxy.ensinfo.sciences.univ-nantes.prive<span class="nt"></host></span>
<span class="nt"><port></span>3128<span class="nt"></port></span>
<span class="nt"></proxy></span>
<span class="nt"></proxies></span>
<span class="nt"></settings></span>
</code></pre></div></div>
<p>Ou alors, si vous préférez la manière simple, vous pouvez tout simplement copier mon fichier de configuration:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cp ~sunye-g/.m2/settings.xml ~/.m2/
</code></pre></div></div>
<p>En complément, sur certaines machines, Maven n’arrive pas à retrouver son dossier d’installation et ne marche pas correctement.
Dans ce cas, vous devez affecter la variable système <code class="highlighter-rouge">M2_HOME</code>:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">M2_HOME</span><span class="o">=</span>/usr/local/opt/maven/
</code></pre></div></div>
<h2 id="-maven-à-liut-de-nantes"><a name="iut"></a> Maven à l’IUT de Nantes</h2>
<p>La configuration du pare-feu de réseau de l’IUT de Nantes pose quelques problèmes au fonctionnement de Maven.
Le premier c’est que tout accès au réseau doit passer par le serveur proxy et Maven ne le sait pas.
Pour le configurer, vous devez créer un fichier appelé <code class="highlighter-rouge">settings.xml</code> dans le répertoire <code class="highlighter-rouge">~/.m2</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>touch ~/.m2/settings.xml
vim ~/.m2/settings.xml
</code></pre></div></div>
<p>Ajoutez ensuite les lignes suivantes:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><settings></span>
<span class="nt"><proxies></span>
<span class="nt"><proxy></span>
<span class="nt"><active></span>true<span class="nt"></active></span>
<span class="nt"><protocol></span>http<span class="nt"></protocol></span>
<span class="nt"><host></span>srv-proxy-etu-2.iut-nantes.univ-nantes.prive<span class="nt"></host></span>
<span class="nt"><port></span>3128<span class="nt"></port></span>
<span class="nt"></proxy></span>
<span class="nt"></proxies></span>
<span class="nt"></settings></span>
</code></pre></div></div>IntroductionAnalyse de mutation avec PIT2017-12-15T10:00:00+00:002017-12-15T10:00:00+00:00https://sunye-g.univ-nantes.io/java/pit/2017/12/15/analyse-mutation<h2 id="introduction">Introduction</h2>
<p>Vous vous rappelez de votre cours de physique où votre professeur a demandé à toute la classe la température d’ébullition de l’eau et que vous,
fier de votre culture vous avez répondu:</p>
<blockquote>
<p>– 100!</p>
</blockquote>
<p>Et quand, sûr de vous, vous attendiez les lauriers de la victoire et le regard admiratif de vos camarades, votre professeur a crié:</p>
<blockquote>
<p>– 100 quoi? Patates? Les unités de mesure, ventrebleu!</p>
</blockquote>
<p>Tous vos camarades ont rit et vous avez rougi.
C’est à ce moment que vous avez commencé à éviter vos camarades, les gens en général, et là, deux solutions s’offraient à vous: aller voir un psy ou devenir informaticien pour développer un programme qui ferait que plus personne n’oublierait les unités! Et ainsi, aucune sonde spatiale ne tomberait par votre faute<sup id="fnref:1"><a href="#fn:1" class="footnote">1</a></sup>.
Et vous vous êtes mis à développer votre propre classe <code class="highlighter-rouge">Mètre</code> que vous comptez utiliser toute votre vie.</p>
<h2 id="la-classe-mètre">La classe «Mètre»</h2>
<p>Vous avez commencé à coder votre classe et avez ajouté une première méthode, <code class="highlighter-rouge">add()</code>, qui permet l’addition de 2 distances.
Bien que son comportement soit simple, cette méthode assure, par exemple, que personne ne pourra faire une addition entre une distance
mesurée en mètres et une autre mesurée en yards:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Meter</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kt">int</span> <span class="n">length</span><span class="o">;</span>
<span class="kd">public</span> <span class="n">Meter</span> <span class="nf">add</span><span class="o">(</span><span class="n">Meter</span> <span class="n">other</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">Meter</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">length</span> <span class="o">+</span> <span class="n">other</span><span class="o">.</span><span class="na">length</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Comme vous êtes soucieux de la qualité de votre code, vous avez écrit un test unitaire pour vérifier votre méthode,
en utilisant le framework de tests <a href="http://junit.org/">JUnit</a>, combiné avec des assertions <a href="http://joel-costigliola.github.io/assertj/">AssertJ</a>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="kt">void</span> <span class="nf">testAdd</span><span class="o">()</span> <span class="o">{</span>
<span class="n">Meter</span> <span class="n">m2</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Meter</span><span class="o">(</span><span class="mi">2</span><span class="o">);</span>
<span class="n">assertThat</span><span class="o">(</span><span class="n">m2</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">m2</span><span class="o">)).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="k">new</span> <span class="n">Meter</span><span class="o">(</span><span class="mi">4</span><span class="o">));</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Après avoir exécuté votre test avec succès, vous vous êtes posé une question:</p>
<blockquote>
<p>– Est-ce que mon test est suffisant ?</p>
</blockquote>
<p>Votre premier réflexe a été de vous retourner vers la couverture de code et à des outils comme <a href="http://cobertura.github.io/cobertura/">Cobertura</a>
ou <a href="http://www.jacoco.org/jacoco/">JaCoCo</a>.
Et le résultat était parfait: votre test couvre 100% des instructions (critères «tous-les-noeuds» et «tous-les-arcs»).
C’est la réussite totale!</p>
<p>Mais votre esprit critique n’était pas satisfait de cette réponse. Car vous avez appris que couvrir 100% des instructions est une bonne chose,
mais cela ne veut pas dire que votre test détecte toutes les erreurs pour autant.
Et que 1 seul test, avec 1 seule donnée de test vous paraît bien insuffisant.</p>
<blockquote>
<p>– Comment puis-je évaluer <em>vraiment</em> la qualité de mes tests? vous êtes-vous demandé une après-midi pendant le goûter.</p>
</blockquote>
<p>Et en regardant le «Blade Runner» Rick Deckard faire passer le test d’empathie <a href="https://fr.wikipedia.org/wiki/Voight-Kampff">Voight-Kampff</a> pour détecter des <em>réplicants</em>, vous vous êtes demandé: et pourquoi pas l’analyse de mutation?</p>
<h2 id="analyse-de-mutation">Analyse de mutation</h2>
<p>L’analyse de mutation est une technique de qualification de cas de tests, qui se base sur l’injection de fautes.
Le principe est simple: on injecte des fautes dans le logiciel sous test et si les tests sont capables de détecter ces fautes, alors leur qualité est bonne.
Et s’ils ne détectent pas ces erreurs, alors ils sont de mauvaise qualité.</p>
<p>Pour simplifier l’évaluation des cas de test et en même temps rendre difficile la détection d’erreurs, on injecte une faute à la fois.
On appelle «mutant» une copie du logiciel sous test à laquelle on a injecté une et une seule faute.</p>
<p>Le choix des fautes à injecter est crucial, car elles ne doivent pas être détectées facilement, mais en même temps, doivent être des vraies fautes.
N’importe quel test peut détecter une erreur d’exécution, comme une division par zéro ou un index hors-plage, par exemple.</p>
<p>On appelle un «opérateur de mutation» une opération qui injecte une faute dans un programme.
Il peut être simple, comme le remplacement d’un opérateur arithmétique <code class="highlighter-rouge">(+,-,*,/,..)</code> par un autre.
Il y a aussi des opérateurs de mutation beaucoup plus complexes, comme l’ajout d’un champ (attribut) qui cacherait un champ hérité d’une super-classe.</p>
<h2 id="les-mutants">Les mutants</h2>
<p>Si l’on applique des opérateurs de mutation arithmétiques à la méthode <code class="highlighter-rouge">add()</code>, on obtient (au moins) quatre mutants.
Tout d’abord, le remplacement de l’addition par une soustraction:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Cyclope</span>
<span class="kd">public</span> <span class="n">Meter</span> <span class="nf">add</span><span class="o">(</span><span class="n">Meter</span> <span class="n">other</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">Meter</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="n">other</span><span class="o">.</span><span class="na">length</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Ensuite, remplacement par une multiplication:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Iceberg</span>
<span class="kd">public</span> <span class="n">Meter</span> <span class="nf">add</span><span class="o">(</span><span class="n">Meter</span> <span class="n">other</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">Meter</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">length</span> <span class="o">*</span> <span class="n">other</span><span class="o">.</span><span class="na">length</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Et par une division:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Mimic</span>
<span class="kd">public</span> <span class="n">Meter</span> <span class="nf">add</span><span class="o">(</span><span class="n">Meter</span> <span class="n">other</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">Meter</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">length</span> <span class="o">/</span> <span class="n">other</span><span class="o">.</span><span class="na">length</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Et finalement, le remplacement de l’addition par l’opération modulo, le reste d’une division euclidienne:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Polaris</span>
<span class="kd">public</span> <span class="n">Meter</span> <span class="nf">add</span><span class="o">(</span><span class="n">Meter</span> <span class="n">other</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">Meter</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">length</span> <span class="o">%</span> <span class="n">other</span><span class="o">.</span><span class="na">length</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Notez que si jamais la méthode <code class="highlighter-rouge">add()</code> contenait une deuxième opération d’addition, on obtiendrait 4 autres mutants, soit 8 au total.</p>
<h2 id="À-la-chasse-aux-mutants">À la chasse aux mutants</h2>
<p>Grâce à ces 4 mutants, on peut qualifier notre test <code class="highlighter-rouge">testAdd()</code>.
Plus précisément, on va tester chacun des mutants.
Si le verdict de l’exécution est un «échec», le test a bien détecté le mutant.
À l’opposé, si le verdict du test est un «succès», le test n’a pas été capable de détecter le mutant.</p>
<p>Lorsqu’on teste les quatre mutants, on obtient le résultat suivant:</p>
<table>
<thead>
<tr>
<th>Mutant</th>
<th style="text-align: center">Résultat évalué</th>
<th style="text-align: center">Résultat attendu</th>
<th style="text-align: center">Verdict</th>
<th style="text-align: center">État du mutant</th>
</tr>
</thead>
<tbody>
<tr>
<td><em>Cyclope</em></td>
<td style="text-align: center">0m</td>
<td style="text-align: center">4m</td>
<td style="text-align: center">Échec</td>
<td style="text-align: center">Mort</td>
</tr>
<tr>
<td><em>Iceberg</em></td>
<td style="text-align: center">4m</td>
<td style="text-align: center">4m</td>
<td style="text-align: center">Succès</td>
<td style="text-align: center">Vivant</td>
</tr>
<tr>
<td><em>Mimic</em></td>
<td style="text-align: center">1m</td>
<td style="text-align: center">4m</td>
<td style="text-align: center">Échec</td>
<td style="text-align: center">Mort</td>
</tr>
<tr>
<td><em>Polaris</em></td>
<td style="text-align: center">0m</td>
<td style="text-align: center">4m</td>
<td style="text-align: center">Échec</td>
<td style="text-align: center">Mort</td>
</tr>
</tbody>
</table>
<p>Le cas de test a été capable de détecter 3 mutants sur 4.
Il a été capable de tuer <em>Cyclope</em>, <em>Mimic</em> et <em>Polaris</em>, mais <em>Iceberg</em> reste vivant.
On parle alors d’un «score de mutation» de 75%.</p>
<p>En d’autres termes, votre cas de test est bon, mais pas aussi bon que la couverture de code le laissait croire.
Vous aurez besoin d’un deuxième cas de test pour atteindre un score de mutation de 100%.
Par exemple, le test suivant vous permettra d’atteindre un score de mutation de 100%:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="kt">void</span> <span class="nf">testAddBis</span><span class="o">()</span> <span class="o">{</span>
<span class="n">Meter</span> <span class="n">m2</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Meter</span><span class="o">(</span><span class="mi">2</span><span class="o">);</span>
<span class="n">Meter</span> <span class="n">m4</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Meter</span><span class="o">(</span><span class="mi">4</span><span class="o">);</span>
<span class="n">assertThat</span><span class="o">(</span><span class="n">m2</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">m4</span><span class="o">)).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="k">new</span> <span class="n">Meter</span><span class="o">(</span><span class="mi">6</span><span class="o">));</span>
<span class="o">}</span>
</code></pre></div></div>
<h2 id="limites-de-lanalyse-de-mutation">Limites de l’analyse de mutation</h2>
<p>Si les résultats de l’analyse de mutation sont plus pertinents que ceux de la couverture de code, son coût est bien plus important.
Le nombre important de mutants créés pour le logiciel sous test, combinés au temps d’exécution du jeu de tests peuvent rendre l’analyse de mutation inapplicable.</p>
<p>De plus, l’analyse de mutation, tout comme la couverture de code, ne peut pas dire si un comportement absent (non mis en œuvre) est correctement testé.
Par exemple, votre méthode <code class="highlighter-rouge">add()</code> contient une erreur importante, elle ne gère pas le débordement d’entiers.
Si le résultat de l’addition est supérieur au plus grand d’entier (<code class="highlighter-rouge">Integer.MAX_VALUE</code>), Java rendra un nombre négatif.</p>
<p>Les techniques de <a href="https://sunye.github.io/testing-courseware/#/5">test fonctionnel</a> et plus précisément le test aux limites, peuvent révéler cette erreur.</p>
<h2 id="pit">PIT</h2>
<p><a href="http://pitest.org">PIT</a> est un outil d’analyse de mutation capable de qualifier des suites de test écrites en JUnit.
Il a été développé par <a href="https://github.com/hcoles">Henry Coles</a> et est distribué sous licence Apache 2.0.</p>
<p>Pour réduire le temps d’exécution et limiter le nombre de mutants générés, PIT combine l’analyse de mutation avec la couverture de code.
Les opérateurs de mutation ne sont appliqués qu’au code couvert par les tests unitaires: PIT ne modifie pas le code qui n’est pas exécuté par les tests.
Aussi, PIT n’exécute que les cas de test qui sensibilisent le code modifié.</p>
<p>Par exemple, votre test unitaire <code class="highlighter-rouge">testAdd()</code> n’exécute que le code de la méthode <code class="highlighter-rouge">add()</code>.
Si PIT crée une centaine de mutants, dont seulement 4 concernent la méthode <code class="highlighter-rouge">add()</code>, le test <code class="highlighter-rouge">testAdd()</code> ne sera exécuté que 4 fois.</p>
<p>PIT s’intègre parfaitement à l’environnement de construction Maven-Gradle-JUnit.
Pour l’utiliser, ajoutez la dépendance suivante à votre fichier <code class="highlighter-rouge">pom.xml</code>:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><dependency></span>
<span class="nt"><groupId></span>org.pitest<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>pitest<span class="nt"></artifactId></span>
<span class="nt"><version></span>1.2.5<span class="nt"></version></span>
<span class="nt"><type></span>pom<span class="nt"></type></span>
<span class="nt"></dependency></span>
</code></pre></div></div>
<p>Ensuite, ajoutez la configuration du plugin <code class="highlighter-rouge">pitest-maven</code> à la phase «build» de Maven,
en spécifiant les paquetages Java qui seront modifiés par PIT:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><plugin></span>
<span class="nt"><groupId></span>org.pitest<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>pitest-maven<span class="nt"></artifactId></span>
<span class="nt"><configuration></span>
<span class="nt"><targetClasses></span>
<span class="nt"><param></span>fr.unantes.info.units.*<span class="nt"></param></span>
<span class="nt"></targetClasses></span>
<span class="nt"><targetTests></span>
<span class="nt"><param></span>fr.unantes.info.units.*<span class="nt"></param></span>
<span class="nt"></targetTests></span>
<span class="nt"></configuration></span>
<span class="nt"><executions></span>
<span class="nt"><execution></span>
<span class="nt"><goals></span>
<span class="nt"><goal></span>mutationCoverage<span class="nt"></goal></span>
<span class="nt"></goals></span>
<span class="nt"><phase></span>pre-site<span class="nt"></phase></span>
<span class="nt"></execution></span>
<span class="nt"></executions></span>
<span class="nt"></plugin></span>
</code></pre></div></div>
<p>Lors que vous utilisez Maven pour créer les rapports de votre projet avec <code class="highlighter-rouge">mvn site</code>, PIT crée un rapport contenant les résultats de l’analyse de mutation.</p>
<h2 id="notes">Notes</h2>
<div class="footnotes">
<ol>
<li id="fn:1">
<p>Mars Climate Orbiter (1998) était une sonde qui s’est écrasée sur Mars en raison d’une <a href="http://www.nirgal.net/mco_end.html">erreur logicielle liée aux unités de mesure</a>. <a href="#fnref:1" class="reversefootnote">↩</a></p>
</li>
</ol>
</div>IntroductionTests paramétrés en JUnit 52017-11-29T10:00:00+00:002017-11-29T10:00:00+00:00https://sunye-g.univ-nantes.io/java/junit/2017/11/29/test-parametres-junit<h2 id="introduction">Introduction</h2>
<p>Ernest Hemingway suggère que pour devenir un être humain accompli, une personne doit réaliser 4 tâches:
1) Planter un arbre, 2) Combattre un taureau, 3) Écrire une nouvelle, 4) Élever un enfant
et 5) Développer sa propre classe Date<sup id="fnref:1"><a href="#fn:1" class="footnote">1</a></sup> en Java.</p>
<p>Si vous voulez accomplir la dernière, vous aurez sans doute besoin de tester unitairement les méthodes de cette classe.
Et rien de plus adapté pour écrire des tests unitaires en Java que <a href="http://junit.org/">JUnit</a> dans sa version la plus récente.</p>
<h2 id="la-classe-date">La classe Date</h2>
<p>La classe <code class="highlighter-rouge">Date</code> que nous allons tester a un constructeur et deux méthodes: <code class="highlighter-rouge">isLeapYear(int)</code> et <code class="highlighter-rouge">int dayOfWeek()</code>.
Cette classe n’est valable que pour les dates postérieures à l’année 1582 et en France.</p>
<p>Pourquoi seulement à partir de cette date? Parce que l’année 1582 est un peu spéciale: elle a durée 341 jours en raison
de l’adoption du calendrier grégorien par Henri III le 9 décembre (jour qui précéda le 20 décembre 1582).
La réforme a été mise en place en France quelques mois après l’Espagne, le Portugal et la Pologne, mais quelques années
avant le Royaume Uni, qui a attendu jusqu’à 1752 pour le faire, et la Russie, qui ne l’a adoptée qu’en 1918.
Pendant plusieurs années, les pays européens n’avaient pas le même calendrier.
Cette confusion a servi de contexte au roman «Le pendule de Foucault» de Umberto Eco, où des sociétés secrètes se sont perdues après un rendez-vous manqué pendant ces années.</p>
<p>Mais revenons à la méthode <code class="highlighter-rouge">isLeapYear(int)</code>. Une première méthode de test pourrait être écrite comme suit:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@Test</span>
<span class="kt">void</span> <span class="nf">testTwoThousandIsALeapYear</span><span class="o">()</span> <span class="o">{</span>
<span class="n">assertThat</span><span class="o">(</span><span class="n">Date</span><span class="o">.</span><span class="na">isLeapYear</span><span class="o">(</span><span class="mi">2000</span><span class="o">)).</span><span class="na">isTrue</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Bien que capable de détecter 1 erreur de codage (si l’année 2000 n’est pas considérée bissextile), cette méthode de test est insuffisante pour détecter d’autres erreurs.
À défaut de pouvoir la tester exhaustivement, on peut utiliser <a href="https://sunye.github.io/testing-courseware/#/5/13">l’analyse partitionnelle</a> pour établir des classes d’équivalence et proposer des données de test efficaces.
Par exemple:</p>
<ul>
<li>Années bissextiles: {1900, 1940, 1996, 2000, 2004}</li>
<li>Années non bissextiles: {1958,1962,1970,1994,2002}</li>
</ul>
<p>Certaines années sont parfois considérés comme bissextiles, alors que ce n’est pas le cas.
Par exemple, les années 1700, 1800 et 1900 ne sont pas bissextiles (parce que divisibles par 100), alors que 1600 et 2000 le sont (car divisibles par 400).
Cela nous permet de trouver 2 autres classes d’équivalence:</p>
<ul>
<li>Années bissextiles inhabituelles: {1600, 2000}</li>
<li>Années non bissextiles inhabituelles: {1700,1800,1900}</li>
</ul>
<p>Pour utiliser ces différentes données de test, nous allons améliorer notre première méthode de test et la rendre paramétrable.</p>
<h2 id="tests-paramétrés">Tests paramétrés</h2>
<p>En Junit 5, les méthodes de test peuvent avoir des paramètres.
Dans ce cas, les méthodes doivent être ornées par l’annotation <code class="highlighter-rouge">@ParameterizedTest</code>, comme suit:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"Valid leap years"</span><span class="o">)</span>
<span class="nd">@ParameterizedTest</span>
<span class="nd">@ValueSource</span><span class="o">(</span><span class="n">ints</span> <span class="o">=</span> <span class="o">{</span><span class="mi">1904</span><span class="o">,</span> <span class="mi">1940</span><span class="o">,</span> <span class="mi">1996</span><span class="o">,</span> <span class="mi">2000</span><span class="o">,</span> <span class="mi">2004</span><span class="o">})</span>
<span class="kt">void</span> <span class="nf">testLeapYear</span><span class="o">(</span><span class="kt">int</span> <span class="n">year</span><span class="o">)</span> <span class="o">{</span>
<span class="n">assertThat</span><span class="o">(</span><span class="n">Date</span><span class="o">.</span><span class="na">isLeapYear</span><span class="o">(</span><span class="n">year</span><span class="o">)).</span><span class="na">isTrue</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Cette méthode de test est aussi ornée par l’annotation <code class="highlighter-rouge">@ValueSource</code> qui dans ce cas, permet de spécifier 5 entiers qui serviront de données de test.
JUnit exécutera cette méthode 5 fois, une pour chaque donnée de test.</p>
<p>Notez que l’annotation <code class="highlighter-rouge">@DisplayName</code> permet de spécifier un nom lisible à la méthode de test.
Notez aussi l’utilisation de <a href="http://joel-costigliola.github.io/assertj/index.html">AssertJ</a>, une des bibliothèques compatibles avec JUnit qui simplifier l’écriture d’assertions.
<a href="http://hamcrest.org">Hamcrest</a> et <a href="http://google.github.io/truth/">Truth</a> sont aussi compatibles avec JUnit.</p>
<p>Nous utilisons ces mêmes annotations pour écrire deux autres méthodes de test pour les années inhabituelles:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"Unusual valid leap years"</span><span class="o">)</span>
<span class="nd">@ParameterizedTest</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">"year: {0}"</span><span class="o">)</span>
<span class="nd">@ValueSource</span><span class="o">(</span><span class="n">ints</span> <span class="o">=</span> <span class="o">{</span><span class="mi">1600</span><span class="o">,</span><span class="mi">2000</span><span class="o">})</span>
<span class="kt">void</span> <span class="nf">testUnusualLeapYear</span><span class="o">(</span><span class="kt">int</span> <span class="n">year</span><span class="o">)</span> <span class="o">{</span>
<span class="n">assertThat</span><span class="o">(</span><span class="n">Date</span><span class="o">.</span><span class="na">isLeapYear</span><span class="o">(</span><span class="n">year</span><span class="o">)).</span><span class="na">isTrue</span><span class="o">();</span>
<span class="o">}</span>
<span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"Unusual invalid leap years"</span><span class="o">)</span>
<span class="nd">@ParameterizedTest</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">"year: {0}"</span><span class="o">)</span>
<span class="nd">@ValueSource</span><span class="o">(</span><span class="n">ints</span> <span class="o">=</span> <span class="o">{</span><span class="mi">1700</span><span class="o">,</span><span class="mi">1800</span><span class="o">,</span><span class="mi">1900</span><span class="o">})</span>
<span class="kt">void</span> <span class="nf">testUnusualNotLeapYear</span><span class="o">(</span><span class="kt">int</span> <span class="n">year</span><span class="o">)</span> <span class="o">{</span>
<span class="n">assertThat</span><span class="o">(</span><span class="n">Date</span><span class="o">.</span><span class="na">isLeapYear</span><span class="o">(</span><span class="n">year</span><span class="o">)).</span><span class="na">isFalse</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<h2 id="méthodes-fournisseuses-de-données">Méthodes fournisseuses de données</h2>
<p>Une alternative au passage de valeurs par l’annotation <code class="highlighter-rouge">@ValueSource</code> est d’appeler une méthode qui retourne un <code class="highlighter-rouge">Stream</code> de données de test.
La méthode doit être définie comme <code class="highlighter-rouge">static</code> et son type de retour doit être compatible avec celui du paramètre de la méthode de test.
La liaison entre la méthode de test est la méthode fournisseuse de données se fait par l’annotation <code class="highlighter-rouge">@MethodSource</code>, qui spécifie le nom de la méthode appelée:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"Invalid leap years"</span><span class="o">)</span>
<span class="nd">@ParameterizedTest</span>
<span class="nd">@MethodSource</span><span class="o">(</span><span class="s">"fiveTimesWorldChampion"</span><span class="o">)</span>
<span class="kt">void</span> <span class="nf">testNotLeapYear</span><span class="o">(</span><span class="kt">int</span> <span class="n">year</span><span class="o">)</span> <span class="o">{</span>
<span class="n">assertThat</span><span class="o">(</span><span class="n">Date</span><span class="o">.</span><span class="na">isLeapYear</span><span class="o">(</span><span class="n">year</span><span class="o">)).</span><span class="na">isFalse</span><span class="o">();</span>
<span class="o">}</span>
<span class="kd">static</span> <span class="n">IntStream</span> <span class="nf">fiveTimesWorldChampion</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">IntStream</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="mi">1958</span><span class="o">,</span><span class="mi">1962</span><span class="o">,</span><span class="mi">1970</span><span class="o">,</span><span class="mi">1994</span><span class="o">,</span><span class="mi">2002</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Pour tester le constructeur de la classe date, <code class="highlighter-rouge">Date(int,int,int)</code> nous à nouveau appliquer l’analyse partitionnelle pour établir deux classes d’équivalence:</p>
<ul>
<li>Dates valides: {10/10/2000, 23/2/1999}</li>
<li>Dates invalides: {10/10/1000, 33/2/1999, 1/13/1999, 0/2/1999, 10/0/1999}</li>
</ul>
<p>Comme le constructeur de la classe sous test prend 3 paramètres, nous allons définir une méthode de test avec 3 paramètres également:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"Constructor: valid dates"</span><span class="o">)</span>
<span class="nd">@ParameterizedTest</span>
<span class="nd">@MethodSource</span><span class="o">(</span><span class="s">"validDates"</span><span class="o">)</span>
<span class="kt">void</span> <span class="nf">testConstructorValidDates</span><span class="o">(</span><span class="kt">int</span> <span class="n">d</span><span class="o">,</span> <span class="kt">int</span> <span class="n">m</span><span class="o">,</span> <span class="kt">int</span> <span class="n">y</span><span class="o">)</span> <span class="o">{</span>
<span class="k">new</span> <span class="nf">Date</span><span class="o">(</span><span class="n">d</span><span class="o">,</span><span class="n">m</span><span class="o">,</span><span class="n">y</span><span class="o">);</span>
<span class="n">assertThat</span><span class="o">(</span><span class="n">date</span><span class="o">.</span><span class="na">year</span><span class="o">()).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="n">y</span><span class="o">);</span>
<span class="n">assertThat</span><span class="o">(</span><span class="n">date</span><span class="o">.</span><span class="na">month</span><span class="o">()).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="n">m</span><span class="o">);</span>
<span class="n">assertThat</span><span class="o">(</span><span class="n">date</span><span class="o">.</span><span class="na">day</span><span class="o">()).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="n">d</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Quand la méthode de test déclare plusieurs paramètres, le données fournies doivent être du type <code class="highlighter-rouge">Argument</code>.
Argument est un n-uplet, où le type de chaque composant correspond au type de chaque paramètre de la méthode de test.
Dans notre exemple, les trois composants sont de type entier:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">static</span> <span class="n">Stream</span><span class="o"><</span><span class="n">Arguments</span><span class="o">></span> <span class="nf">validDates</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">Stream</span><span class="o">.</span><span class="na">of</span><span class="o">(</span>
<span class="n">Arguments</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="mi">10</span><span class="o">,</span> <span class="mi">10</span><span class="o">,</span> <span class="mi">2000</span><span class="o">),</span>
<span class="n">Arguments</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="mi">23</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="mi">1999</span><span class="o">)</span>
<span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Une troisième et dernière façon de passer des données à une méthode de test consiste à utiliser l’annotation <code class="highlighter-rouge">@CsvSource</code>, qui permet de spécifier un ensemble de chaînes de caractères, où chaque chaine contient des arguments au format CSV (séparés par des virgules):</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"Constructor: invalid dates"</span><span class="o">)</span>
<span class="nd">@ParameterizedTest</span>
<span class="nd">@CsvSource</span><span class="o">({</span><span class="s">"10,10,1000"</span><span class="o">,</span><span class="s">"33,2,1999"</span><span class="o">,</span><span class="s">"1,13,1999"</span><span class="o">,</span><span class="s">"0,2,1999"</span><span class="o">,</span><span class="s">"10,0,1999"</span><span class="o">})</span>
<span class="kt">void</span> <span class="nf">testConstructotInvalidDates</span><span class="o">(</span><span class="kt">int</span> <span class="n">d</span><span class="o">,</span> <span class="kt">int</span> <span class="n">m</span><span class="o">,</span> <span class="kt">int</span> <span class="n">y</span><span class="o">)</span> <span class="o">{</span>
<span class="n">assertThrows</span><span class="o">(</span><span class="n">IllegalArgumentException</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="o">()</span> <span class="o">-></span> <span class="k">new</span> <span class="n">Date</span><span class="o">(</span><span class="n">d</span><span class="o">,</span><span class="n">m</span><span class="o">,</span><span class="n">y</span><span class="o">));</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Notez que cette méthode de test utilise l’assertion <code class="highlighter-rouge">assertThrows()</code>, introduite dans la version 5 de JUnit et qui fait appel aux expressions lambda de Java 8.
Son comportement est simple: elle attend qu’une exception du type passé en premier paramètre soit levée lors de l’évaluation de l’expression passée en deuxième paramètre.</p>
<h2 id="code-source">Code source</h2>
<p>Le code source de cet exemple est disponible sur le <a href="https://github.com/sunye/blog-examples/tree/master/parameterized-tests">GitHub</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>JUnit permet la séparation entre les méthodes de test et les données de tests:</p>
<ul>
<li>Les méthodes de test peuvent avoir des paramètres;</li>
<li>Les données de test sont décrites soit à l’intérieur d’une annotation, soit par une méthode externe, appelée <em>fournisseuse de données</em>.</li>
<li>Les méthodes de test sont appelées autant de fois qu’il y a de données de test.</li>
</ul>
<h2 id="notes">Notes</h2>
<div class="footnotes">
<ol>
<li id="fn:1">
<p>Cette dernière suscite de vives controverses. <a href="#fnref:1" class="reversefootnote">↩</a></p>
</li>
</ol>
</div>IntroductionInjection de dépendance2017-11-21T12:52:25+00:002017-11-21T12:52:25+00:00https://sunye-g.univ-nantes.io/java/spring/2017/11/21/injection-dependance<h2 id="introduction">Introduction</h2>
<p>Vous vous êtes déjà fait taper sur les doigts par votre instructeur de Java préféré lorsque vous avez déclaré une liste d’éléments de la façon suivante:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyClass</span> <span class="o">{</span>
<span class="kd">private</span> <span class="n">ArrayList</span><span class="o"><</span><span class="n">String</span><span class="o">></span> <span class="n">tags</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Vous n’avez peut-être pas compris, mais il avait raison.
Votre code ne doit pas dépendre d’une classe concrète lorsqu’il n’a besoin que des méthodes déclarés par une interface, comme c’est le cas ici.
Vous avez donc modifié votre code, pour essayer de ne dépendre que de l’interface <code class="highlighter-rouge">List</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyClass</span> <span class="o">{</span>
<span class="kd">private</span> <span class="n">List</span><span class="o"><</span><span class="n">String</span><span class="o">></span> <span class="n">tags</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p>En effet, dans ce cas, modifier le type déclaré des champs, paramètres, variables, etc. est simple et réduit le couplage:
ces propriétés ne dépendront que de l’interface <code class="highlighter-rouge">List</code> et non de ses différentes implémentation.
Cependant, lors de l’initialisation d’un champ ou d’une variable, vous ne pouvez faire autrement qu’utiliser la classe concrète:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nf">MyClass</span><span class="o">()</span> <span class="o">{</span>
<span class="n">tags</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ArrayList</span><span class="o"><</span><span class="n">String</span><span class="o">>();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Cette unique utilisation de <code class="highlighter-rouge">ArrayList</code> empêche la classe <code class="highlighter-rouge">MyClass</code> d’être complètement indépendante des classes concrètes: elle continue de
dépendre de l’interface <code class="highlighter-rouge">List</code> et de son implémentation.</p>
<p>Une solution simple à ce problème, c’est d’instancier la liste d’éléments à l’extérieur de la classe et de la passer comme paramètre du constructeur:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyClass</span> <span class="o">{</span>
<span class="kd">private</span> <span class="n">List</span><span class="o"><</span><span class="n">String</span><span class="o">></span> <span class="n">tags</span><span class="o">;</span>
<span class="kd">public</span> <span class="nf">MyClass</span><span class="o">(</span><span class="n">List</span> <span class="n">l</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">tags</span> <span class="o">=</span> <span class="n">l</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Si cette solution résout partiellement le problème et peut améliorer la testabilité de la classe: les cas de test pourront la configurer de
l’extérieur et ensuite contrôler et observer son comportement, grâce aux doublures de test.</p>
<p>Mais la solution rend l’utilisation de la classe plus complexe:
tout client souhaitant l’utiliser devra d’abord instancier une liste et la passer comme argument.
Et si plusieurs champs doivent être instanciés à l’extérieur de la classe, le code deviendra trop complexe.</p>
<p>Une autre solution similaire et tout aussi partielle, consiste à utiliser une méthode pour initialiser la liste d’éléments:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyClass</span> <span class="o">{</span>
<span class="kd">private</span> <span class="n">List</span><span class="o"><</span><span class="n">String</span><span class="o">></span> <span class="n">tags</span><span class="o">;</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setTags</span><span class="o">(</span><span class="n">List</span> <span class="n">l</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">tags</span> <span class="o">=</span> <span class="n">l</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Cette solution non seulement présente les mêmes inconvénients que la précédente, comme aussi en introduit un nouveau:
le client doit être au courant que cette méthode doit être appelée avant l’utilisation effective d’une instance de cette classe.</p>
<h2 id="le-pattern-factory-à-la-rescousse">Le pattern «Factory» à la rescousse</h2>
<p>Une solution intéressante pour simplifier l’initialisation de la classe <code class="highlighter-rouge">MyClass</code> est apportée par les patrons de conception.
En effet, le patron <em>Factory</em> permet de simplifier la création d’objets dont la configuration est complexe.
L’idée de ce pattern est d’utiliser une autre classe ayant pour seul objectif la création d’objects dont elle est responsable
pour les fournir prêt-à-servir; la classe concrète utilisée n’étant pas connue par l’appelant.</p>
<p>Une implémentation possible de la solution est listée ci-dessous:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyClassFactory</span> <span class="o">{</span>
<span class="kd">public</span> <span class="n">MyClass</span> <span class="nf">createMyClass</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">MyClass</span><span class="o">(</span><span class="k">new</span> <span class="n">ArrayList</span><span class="o">());</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Ici, la fabrique se charge de configurer la classe <code class="highlighter-rouge">MyClass</code> lors de sa création.
<code class="highlighter-rouge">MyClass</code> devient enfin complètement indépendante de l’implémentation de la classe <code class="highlighter-rouge">List</code>.</p>
<p>Une autre solution pour simplifier l’utilisation de MyClass consiste à utiliser l’injection de dépendance,
qui nous permettra de nous passer des fabriques, tout en gardant les avantages.</p>
<h2 id="injection-de-dépendance">Injection de dépendance</h2>
<p>L’injection de dépendance consiste à faire appel à un mécanisme externe à la classe, qui va se charger d’initialiser ses attributs,
ou en d’autres termes, va se charger d’injecter automatiquement des dépendances vers des classes concrètes.
Il existe plusieurs mécanismes d’injection de dépendance. Un de plus connus et aussi le plus utilisé est <a href="https://spring.io">Spring</a>,
mais d’autres mécanismes plus légers et récents sont aussi disponibles: <a href="https://github.com/google/guice">Google Guice</a>,
<a href="http://square.github.io/dagger/">Dagger</a>, <a href="http://picocontainer.com/">PicoContainer</a> ou <a href="http://weld.cdi-spec.org">Weld</a>.
Dans nos exemples, nous utiliserons Spring.</p>
<h3 id="spécification-du-point-dinjection">Spécification du point d’injection</h3>
<p>Dans notre exemple, le rôle de l’injection de dépendance sera d’initialiser automatiquement l’attribut <code class="highlighter-rouge">tags</code>.
Cependant, il n’y a pas de magie: on devra spécifier quels attributs doivent être initialisés et comment.
Pour ce faire, on utilisera deux spécificités du langage Java: les annotations et la réflexion.
Tout d’abord, nous allons utiliser une des annotations définies par la <a href="http://javax-inject.github.io/javax-inject/">JSR-330</a>,
<code class="highlighter-rouge">@Inject</code> pour spécifier que l’attribut <code class="highlighter-rouge">tags</code> doit être initialisé automatiquement:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">javax.inject.Inject</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyClass</span> <span class="o">{</span>
<span class="nd">@Inject</span>
<span class="kd">private</span> <span class="n">List</span><span class="o"><</span><span class="n">String</span><span class="o">></span> <span class="n">tags</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<h3 id="configuration">Configuration</h3>
<p>Ensuite, la réflexion sera utilisée par une <em>Configuration</em> (dans le jargon Spring)
ou un <em>Module</em> (dans le jargon Guice)
pour trouver les attributs qui seront initialisés automatiquement, ainsi que leur types, pour ensuite les initialiser.
Dans le cas de Spring, la configuration devra spécifier une méthode compatible avec le type de l’attribut <code class="highlighter-rouge">tags</code>, <code class="highlighter-rouge">List<String></code>.
Voici un exemple de configuration Spring:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Configuration</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyConfiguration</span> <span class="o">{</span>
<span class="nd">@Bean</span>
<span class="kd">public</span> <span class="n">MyClass</span> <span class="nf">myClass</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">MyClass</span><span class="o">();</span>
<span class="o">}</span>
<span class="nd">@Bean</span>
<span class="kd">public</span> <span class="n">List</span><span class="o"><</span><span class="n">String</span><span class="o">></span> <span class="nf">produceList</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="n">LinkedList</span><span class="o"><</span><span class="n">String</span><span class="o">>();</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Dans cet exemple, le type de retour de la méthode <code class="highlighter-rouge">produceList()</code> fait de celle-ci la seule candidate pour initialiser la variable <code class="highlighter-rouge">tags</code>.</p>
<p>Notez que cette configuration n’est pas spécifique à la classe <code class="highlighter-rouge">MyClass</code>, elle peut servir à d’autres classes.</p>
<h3 id="utilisation">Utilisation</h3>
<p>L’utilisation de la classe <code class="highlighter-rouge">MyClass</code> doit passer par un <em>Contexte</em>, qui utilisera la configuration pour créer des instances de <code class="highlighter-rouge">MyClass</code></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
<span class="n">ApplicationContext</span> <span class="n">ctx</span> <span class="o">=</span> <span class="k">new</span> <span class="n">AnnotationConfigApplicationContext</span><span class="o">(</span><span class="n">MyConfiguration</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="n">MyClass</span> <span class="n">mine</span> <span class="o">=</span> <span class="o">(</span><span class="n">MyClass</span><span class="o">)</span> <span class="n">ctx</span><span class="o">.</span><span class="na">getBean</span><span class="o">(</span><span class="n">MyClass</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Lorsque le contexte initialise une instance de <code class="highlighter-rouge">MyClass</code>, grâce à la méthode <code class="highlighter-rouge">getBean()</code>, il initialisera automatiquement l’attribut <code class="highlighter-rouge">tags</code> grâce à la
méthode <code class="highlighter-rouge">produceList()</code> de la classe <code class="highlighter-rouge">MyConfiguration</code>.
Ici, le nom de la méthode n’est pas important, ce qui compte c’est le type de retour. Ainsi, pour initialiser un attribut de la classe <code class="highlighter-rouge">List</code>,
Spring cherchera les méthodes de création (<code class="highlighter-rouge">@Bean</code>) dont le type de retour est compatible avec <code class="highlighter-rouge">List</code>.</p>
<p>On peut utiliser cette configuration également pour initialiser l’attribut avec une autre implémentation de <code class="highlighter-rouge">List</code> et même pour ajouter quelques valeurs à cette liste.</p>
<h3 id="code-source">Code source</h3>
<p>Le code source de cet exemple est disponible sur le <a href="https://github.com/sunye/blog-examples/tree/master/dependency-injection">GitHub</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Grâce à l’injection de dépendance, il est possible de séparer l’initialisation des attributs de leur utilisation.
Dans notre exemple, les conséquences sont les suivantes:</p>
<ol>
<li>La classe <code class="highlighter-rouge">MyClass</code> ne connaît pas la classe concrète que l’attribut <code class="highlighter-rouge">tags</code> utilise: elle ne dépend que de l’interface <code class="highlighter-rouge">List</code>.</li>
<li>Les classes qui utilisent <code class="highlighter-rouge">MyClass</code> (les clientes) n’ont pas besoin de la configurer pour l’utiliser.</li>
<li>La liaison entre l’attribut de type <code class="highlighter-rouge">List</code> et la classe concrète <code class="highlighter-rouge">LinkedList</code> est fait par une classe tiers de configuration.</li>
<li>Lorsqu’on utilise Spring pour l’injection de dépendance, il est possible de remplacer la classe de configuration par un fichier XML.</li>
</ol>Introduction