<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Nicolas Gauthier</title>
    <description>The latest articles on Forem by Nicolas Gauthier (@n-g-a).</description>
    <link>https://forem.com/n-g-a</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2273645%2Fc2d0812e-ceff-4584-a634-316f8966e1ce.jpg</url>
      <title>Forem: Nicolas Gauthier</title>
      <link>https://forem.com/n-g-a</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/n-g-a"/>
    <language>en</language>
    <item>
      <title>La collaboration : fondement de l'architecture logicielle</title>
      <dc:creator>Nicolas Gauthier</dc:creator>
      <pubDate>Tue, 29 Jul 2025 07:26:56 +0000</pubDate>
      <link>https://forem.com/onepoint/la-collaboration-fondement-de-larchitecture-logicielle-3mk3</link>
      <guid>https://forem.com/onepoint/la-collaboration-fondement-de-larchitecture-logicielle-3mk3</guid>
      <description>&lt;p&gt;La conception d'un système logiciel repose avant tout sur la compréhension fine des interactions entre ses différents éléments constitutifs. Qu'il s'agisse de systèmes distribués, de modules au sein d'une application ou d'objets dans un paradigme orienté objet, la collaboration représente le cœur même de l'architecture logicielle.&lt;/p&gt;

&lt;h2&gt;
  
  
  La collaboration comme pierre angulaire
&lt;/h2&gt;

&lt;p&gt;Les systèmes logiciels sont constitués d'éléments plus ou moins complexes qui communiquent entre eux, échangent des données et coordonnent leurs actions pour accomplir des objectifs. Cette collaboration se manifeste à travers différents mécanismes : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;appels de méthodes, &lt;/li&gt;
&lt;li&gt;échanges de messages, &lt;/li&gt;
&lt;li&gt;partage de ressources, &lt;/li&gt;
&lt;li&gt;synchronisation d'états &lt;/li&gt;
&lt;li&gt;ou encore notification d'événements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La qualité de ces interactions détermine directement les propriétés émergentes du système final : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sa robustesse, &lt;/li&gt;
&lt;li&gt;sa maintenabilité, &lt;/li&gt;
&lt;li&gt;ses performances &lt;/li&gt;
&lt;li&gt;et sa capacité d'évolution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Une collaboration mal conçue peut transformer un ensemble de composants individuellement fonctionnels en un système fragile et imprévisible.&lt;/p&gt;

&lt;h2&gt;
  
  
  L'essence de la stabilité et de la volatilité
&lt;/h2&gt;

&lt;p&gt;Avant d'aborder toute question architecturale, il est crucial de caractériser la nature des collaborations selon leur degré de stabilité. Cette analyse permet de distinguer deux catégories fondamentales d'interactions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Collaborations stables
&lt;/h3&gt;

&lt;p&gt;Les collaborations &lt;strong&gt;stables&lt;/strong&gt; correspondent aux interactions dont le comportement est prévisible et déterministe. Un collaborateur stable est celui dont les réactions peuvent être anticipées et maîtrisées par le système qui l'utilise. Il répond de manière cohérente aux mêmes sollicitations et ses comportements restent dans le périmètre de contrôle du système.&lt;/p&gt;

&lt;h4&gt;
  
  
  ⚙️ Cas d'usage
&lt;/h4&gt;

&lt;p&gt;Prenons l'exemple suivant, permettant à un système de transférer une somme d'argent entre deux comptes :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TransferMoneyUseCase&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withdraw&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deposit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;withdraw&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compareTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ZERO&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalAmountException&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compareTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InsufficientFundsException&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subtract&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;deposit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compareTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ZERO&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalAmountException&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="nf"&gt;getBalance&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cette opération de transfert est réalisée par la classe &lt;code&gt;TransferMoneyUseCase&lt;/code&gt;, laquelle collabore (par l'intermédiaire de sa méthode &lt;code&gt;transfer&lt;/code&gt;) avec les objets &lt;code&gt;Account&lt;/code&gt; et &lt;code&gt;BigDecimal&lt;/code&gt; pour effectuer la transaction.&lt;/p&gt;

&lt;p&gt;Intéressons-nous particulièrement au collaborateur &lt;code&gt;Account&lt;/code&gt;. Comment peut-on qualifier les intéractions initiées par &lt;code&gt;TransferMoneyUseCase&lt;/code&gt; ?&lt;/p&gt;

&lt;p&gt;➡️ &lt;strong&gt;Prévisibles et déterministes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;L’exécution des méthodes &lt;code&gt;withdraw&lt;/code&gt; et &lt;code&gt;deposit&lt;/code&gt; sur les instances d'&lt;code&gt;Account&lt;/code&gt; donne toujours le même résultat pour un même contexte d'appel. Les cas limites sont clairement prévisibles et gérés par des exceptions métier explicites (&lt;code&gt;IllegalAmountException&lt;/code&gt;, &lt;code&gt;InsufficientFundsException&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;➡️ &lt;strong&gt;Contrôlable par le système&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La méthode &lt;code&gt;transfer&lt;/code&gt; interagit directement avec une API publique maîtrisée (&lt;code&gt;withdraw&lt;/code&gt;, &lt;code&gt;deposit&lt;/code&gt;), sur des objets &lt;code&gt;Account&lt;/code&gt; contrôlables à travers de :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;leur état initial (le solde avant toute transaction),&lt;/li&gt;
&lt;li&gt;leurs mécanismes d'interaction (les méthodes exposées),&lt;/li&gt;
&lt;li&gt;leur état observable (le solde courant).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;➡️ &lt;strong&gt;Sans interférence&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Aucun effet secondaire non maîtrisé ne vient interférer lors de l'exécution : pas d'accès réseau, pas de base de données, pas d'élément asynchrone ou aléatoire. La classe &lt;code&gt;Account&lt;/code&gt; encapsule les règles métier et reste indépendante de tout composant externe incontrôlable et imprévisible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Collaborations volatiles
&lt;/h3&gt;

&lt;p&gt;À l'opposé, les collaborations &lt;strong&gt;volatiles&lt;/strong&gt; se caractérisent par leur imprévisibilité et échappent au contrôle du système. Elles concernent généralement les aspects périphériques, les intégrations avec des services externes, la gestion de l'aléatoire, la temporalité, etc. Leur comportement ne peut être ni anticipé ni maîtrisé par le système qui les utilise. Ces zones d'intéraction nécessitent alors une approche architecturale différente.&lt;/p&gt;

&lt;h4&gt;
  
  
  ⚙️ Cas d'usage
&lt;/h4&gt;

&lt;p&gt;Poursuivons avec notre cas d'usage précédent. Les opérations effectuées par le système lors du transfert nécessitent maintenant d'être sauvegardées dans un système externe (une base de données par exemple). Ce système, par sa nature, échappe au contrôle du système appelant. La collaboration devient alors non maitrisée.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TransferMoneyUseCase&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;AccountRepository&lt;/span&gt; &lt;span class="n"&gt;accountRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;TransferMoneyUseCase&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;accountRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AccountRepository&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withdraw&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deposit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;accountRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;accountRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dans cet exemple, &lt;code&gt;TransferMoneyUseCase&lt;/code&gt; dépend désormais d’un composant externe, &lt;code&gt;AccountRepository&lt;/code&gt;, en charge de la persistence des comptes. Or, la base de données utilisée par ce repository n’est pas contrôlée par le système, ce qui introduit des effets de bord potentiels (état initial incohérent, erreur réseau, latence, indisponibilité, etc.).&lt;/p&gt;

&lt;p&gt;Comment peut-on qualifier ces nouvelles intéractions ?&lt;/p&gt;

&lt;p&gt;➡️ &lt;strong&gt;Imprévisibles par nature&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La persistance dépend d’un système tiers, dont le comportement peut varier indépendamment du code métier (pannes, délais, erreurs de connexion...).&lt;/p&gt;

&lt;p&gt;➡️ &lt;strong&gt;Non déterministes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Avec les mêmes entrées, le résultat de l'opération peut différer d'un appel à l'autre :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rien ne garantie de l'existence préalable des comptes sur le système externe, celui-ci peut alors lever une exception.&lt;/li&gt;
&lt;li&gt;La concurrence d'accès aux données modifiées peut produire des erreurs (dans le cas d'un verrouillage optimiste ou pessimiste par exemple).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;➡️ &lt;strong&gt;Difficiles à tester directement&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tester cette collaboration nécessite soit d’accéder à un système réel (ce qui introduit de la lenteur et de la fragilité), soit de substituer le collaborateur (par exemple via un test double).&lt;/p&gt;

&lt;h3&gt;
  
  
  Complexité de l'identification des collaborations volatiles
&lt;/h3&gt;

&lt;p&gt;L'exemple de collaboration volatile présenté précédemment constitue un cas typique, relativement facile à repérer. Cependant, d'autres situations s'avèrent plus délicates à identifier (aléatoire, temporalité, états partagés, etc.).&lt;/p&gt;

&lt;p&gt;En amont de toute conception architecturale, il sera &lt;strong&gt;essentiel&lt;/strong&gt; d'examiner ces collaborations volatiles et d'évaluer leur &lt;strong&gt;criticité&lt;/strong&gt; en fonction de la capacité du système à les maîtriser.&lt;/p&gt;

&lt;p&gt;Nous évoquerons plus en détail ces cas et leurs répercussions sur le système lorsque nous aborderons le chapître sur la &lt;strong&gt;répercussion sur la stratégie de tests&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implications architecturales
&lt;/h2&gt;

&lt;p&gt;Cette distinction entre contrôlabilité et imprévisibilité guide directement les choix architecturaux. Pour les collaborateurs volatils, l'architecture &lt;strong&gt;peut bénéficier&lt;/strong&gt; de points de substitution permettant de remplacer ces éléments imprévisibles par des équivalents contrôlables (lors des tests par exemple). Cette approche s'avère particulièrement pertinente lorsque la volatilité compromet significativement la testabilité ou la robustesse du système.&lt;/p&gt;

&lt;p&gt;C'est dans ce contexte qu'intervient l'&lt;em&gt;inversion de dépendances&lt;/em&gt; : plutôt que de dépendre directement d'un collaborateur volatil, le système dépend d'une abstraction qu'il contrôle. Cette inversion transforme une dépendance subie (vers quelque chose d'imprévisible) en une dépendance maîtrisée (vers une interface sous contrôle). Le collaborateur volatil devient alors un détail d'implémentation injecté de l'extérieur, substituable selon les besoins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La décision d'introduire cette abstraction doit néanmoins être équilibrée&lt;/strong&gt; : elle apporte de la flexibilité et de la testabilité au prix d'une complexité architecturale supplémentaire. Dans certains cas, accepter une volatilité limitée peut s'avérer plus pragmatique que multiplier les couches d'abstraction.&lt;/p&gt;

&lt;p&gt;Les zones stables adoptent en général des couplages plus directs, car leur comportement déterministe ne compromet pas la robustesse du système.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚙️ Cas d'usage
&lt;/h3&gt;

&lt;p&gt;Reprenons avec notre exemple. Le collaborateur chargé de persister l'état des comptes étant volatile, notre architecture subit quelques modifications :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;AccountRepository&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;AccountRepository&lt;/code&gt; est ici une interface contractualisant une intension : Sauvegarder un compte.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JpaAccountRepository&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@PersistenceContext&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;EntityManager&lt;/span&gt; &lt;span class="n"&gt;entityManager&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Transactional&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;AccountEntity&lt;/span&gt; &lt;span class="n"&gt;accountEntity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entityManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;find&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AccountEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;accountEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setBalance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBalance&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;JpaAccountRepository&lt;/code&gt; est une implémentation concrète de l'interface &lt;code&gt;AccountRepository&lt;/code&gt;. Elle aura en charge de persister les données de comptes dans le système externe.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TransferMoneyUseCase&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;AccountRepository&lt;/span&gt; &lt;span class="n"&gt;accountRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;TransferMoneyUseCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AccountRepository&lt;/span&gt; &lt;span class="n"&gt;accountRepository&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;accountRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accountRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La classe &lt;code&gt;TransferMoneyUseCase&lt;/code&gt; requiert désormais l'injection d'un collaborateur implémentant l'interface &lt;code&gt;AccountRepository&lt;/code&gt;. Elle ne dépend plus de son implémentation réelle, mais impose par un contrat la façon dont elle interagira avec le système externe pour sauvegarder les comptes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;AccountRepository&lt;/span&gt; &lt;span class="n"&gt;accountRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JpaAccountrepository&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;TransferMoneyUseCase&lt;/span&gt; &lt;span class="n"&gt;transferMoneyUseCase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TransferMoneyUseCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountRepository&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;L'implémentation concrète &lt;code&gt;JpaAccountRepository&lt;/code&gt; est injectée à l'instanciation de notre classe &lt;code&gt;TransferMoneyUseCase&lt;/code&gt;, mais ceci devient désormais un détail pour notre cas d'usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Répercussions sur la stratégie de tests
&lt;/h2&gt;

&lt;p&gt;La nature des collaborations influence directement la stratégie de test à adopter. &lt;/p&gt;

&lt;p&gt;Les collaborateurs stables, étant déterministes et contrôlables, peuvent être conservés dans les tests unitaires, car ils n'introduisent pas d'aléa. Leur présence renforce même la confiance en ces tests puisqu'ils valident les interactions réelles.&lt;/p&gt;

&lt;p&gt;Les collaborateurs volatils quant-à-eux doivent être substitués, car leur imprévisibilité compromettrait la fiabilité des tests. Un test unitaire qui dépend d'un service externe imprévisible n'est plus vraiment unitaire puisqu'il n'en respecte pas une des caractéristiques fondamentales : sa reproductibilité.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚙️ Cas d'usage
&lt;/h3&gt;

&lt;p&gt;Reprenons notre cas d'usage encore une fois. Comment tester désormais de manière prévisible et contrôlée notre système ? C'est là qu'interviennent les tests doubles. Il en existe différents types, mais ce n'est pas le sujet de cet article.&lt;/p&gt;

&lt;p&gt;Commençons tout d'abord par vérifier les collaborations stables de notre cas d'usage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AccountRepositoryStub&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;AccountRepository&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// do nothing&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;shouldTransferMoneyBetweenAccounts&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Arrange&lt;/span&gt;
    &lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;issuer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;randomUUID&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"100.00"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;receiver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;randomUUID&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"50.00"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="nc"&gt;AccountRepository&lt;/span&gt; &lt;span class="n"&gt;accountRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AccountRepositoryStub&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;TransferMoneyUseCase&lt;/span&gt; &lt;span class="n"&gt;useCase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TransferMoneyUseCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountRepository&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Act&lt;/span&gt;
    &lt;span class="n"&gt;useCase&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;transfer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"30.00"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Assert&lt;/span&gt;
    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"70.00"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="n"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBalance&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"80.00"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBalance&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dans cet exemple, nous ne vérifions que les collaborations stables, par l'intermédiaire des changements d'états de nos objets &lt;code&gt;Account&lt;/code&gt; après la transaction. Utiliser le collaborateur réel pour gérer la persistence nuirait au bon fonctionnement de notre test, c'est pourquoi nous le substituons par un &lt;code&gt;Stub&lt;/code&gt; (&lt;code&gt;AccountRepositoryStub&lt;/code&gt;) respectant le contrat.&lt;/p&gt;

&lt;p&gt;Comment maintenant vérifier que les changements d'état de nos comptes ont bien été sauvegardés dans notre système externe ?&lt;/p&gt;

&lt;p&gt;La responsabilité de la sauvegarde physique des données n'est pas à la charge de notre classe &lt;code&gt;TransferMoneyUseCase&lt;/code&gt;. Par contre, il est de sa responsabilité d'interagir avec &lt;code&gt;AccountRepository&lt;/code&gt; pour effectuer cette sauvegarde. Nous pourrions alors substituer le collaborateur &lt;code&gt;AccountRepository&lt;/code&gt; par un &lt;code&gt;Fake&lt;/code&gt;, afin de vérifier que le système cible a pris en charge la modification.&lt;/p&gt;

&lt;p&gt;Ceci pourrait donner :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InMemoryAccountRepository&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;AccountRepository&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;InMemoryAccountRepository&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;shouldSaveAccountsAfterTransfer&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Arrange&lt;/span&gt;
    &lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;issuer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;randomUUID&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"100.00"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;receiver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;randomUUID&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"50.00"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;AccountRepository&lt;/span&gt; &lt;span class="n"&gt;accountRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InMemoryAccountRepository&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;TransferMoneyUseCase&lt;/span&gt; &lt;span class="n"&gt;useCase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TransferMoneyUseCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountRepository&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Act&lt;/span&gt;
    &lt;span class="n"&gt;useCase&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;transfer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"30.00"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Assert&lt;/span&gt;
    &lt;span class="n"&gt;assertTrue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"70.00"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;getBalance&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;assertTrue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"80.00"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;getBalance&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le système externe est désormais simulé par l'implémentation mémoire &lt;code&gt;InMemoryAccountRepository&lt;/code&gt; et reproduit de façon minimale le comportement du collaborateur réel &lt;code&gt;JpaAccountRepository&lt;/code&gt;. Ce collaborateur est totalement contrôlé et son fonctionnement devient prévisible. Notre test devient alors fiable et robuste, et découplé de l'implémentation réelle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conserver le contrôle du système lors des tests
&lt;/h3&gt;

&lt;p&gt;Nous l'avons largement évoqué, l'imprévisibilité conduit forcément à une perte de contrôle du système.&lt;/p&gt;

&lt;p&gt;Bien que les collaborations avec des systèmes externes constituent une grande majorité de cas prévus pour être substituables facilement, d'autres sont plus difficilement identifiables et ont un impact direct sur la testabilité.&lt;/p&gt;

&lt;p&gt;La testabilité est importante, mais elle ne justifie pas systématiquement l’introduction de complexité : chaque décision de substitution doit s’inscrire dans une logique de compromis éclairé.&lt;/p&gt;

&lt;p&gt;En effet, &lt;strong&gt;des alternatives à la substitution existent&lt;/strong&gt; : environnements de test contrôlés (containers), isolation des effets de bord dans des couches dédiées, ou encore outils spécialisés pour gérer certains types de volatilité (environnement, date/zone, etc...). Ces approches peuvent parfois offrir un meilleur équilibre entre simplicité architecturale et contrôle de la testabilité, mais ne seront pas abordées en détail dans cet article.&lt;/p&gt;

&lt;h4&gt;
  
  
  L'aléatoire
&lt;/h4&gt;

&lt;p&gt;Toute génération de données aléatoires introduit de l'imprévisibilité dans le système. Qu’il s’agisse de la création d’un identifiant unique (&lt;code&gt;UUID&lt;/code&gt;), d’une chaîne de caractères, d’un entier pseudo-aléatoire ou d’une opération mathématique incluant une part de hasard, chaque appel peut produire un résultat différent. Cette variabilité fragilise le raisonnement autour du comportement attendu et rend les tests non reproductibles.&lt;/p&gt;

&lt;p&gt;Pour restaurer la prédictibilité dans les tests, il pourrait être judicieux d’introduire une dépendance injectable.&lt;/p&gt;

&lt;p&gt;Par exemple :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Inversion de la dépendance entre le système et le collaborateur réel&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;UUIDProvider&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Implémentation réelle injectée par défaut dans le système&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RandomUUIDProvider&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;UUIDProvider&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;randomUUID&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Implémentation contrôlable, injecté depuis les tests.&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FixedUUIDProvider&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;UUIDProvider&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="n"&gt;fixedUUID&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;FixedUUIDProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="n"&gt;fixedUUID&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fixedUUID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fixedUUID&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fixedUUID&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  La temporalité
&lt;/h4&gt;

&lt;p&gt;Les dépendances temporelles constituent une autre source majeure de volatilité. Lorsqu’un système s’appuie sur la date ou l’heure (date courante, date fixée, jour de la semaine, heure du jour, etc.), son comportement devient dépendant du moment d’exécution. Ce type de dépendance rend les tests &lt;strong&gt;non déterministes&lt;/strong&gt; (ils peuvent échouer ou réussir selon le moment), et empêche toute validation fiable sans contrôle du temps.&lt;/p&gt;

&lt;p&gt;Comme précédemment, il conviendra éventuellement de prévoir un mécanisme de substitution.&lt;/p&gt;

&lt;p&gt;Par exemple :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Inversion de la dépendance entre le système et le collaborateur réel&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ClockProvider&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Implémentation réelle injectée par défaut dans le système&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SystemClockProvider&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ClockProvider&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Implémentation contrôlable, injecté depuis les tests.&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FixedClockProvider&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ClockProvider&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;fixedNow&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;FixedClockProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;fixedNow&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fixedNow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fixedNow&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fixedNow&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  L'environnement
&lt;/h4&gt;

&lt;p&gt;L’environnement d’exécution peut influencer le comportement d’un composant de manière invisible, mais significative. Cela inclut :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;les &lt;strong&gt;variables d’environnement&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;les &lt;strong&gt;fichiers de configuration&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;les &lt;strong&gt;paramètres système&lt;/strong&gt; (fuseau horaire, langue, encoding),&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tout accès implicite à ces éléments introduit de la &lt;strong&gt;variabilité externe&lt;/strong&gt;, difficile à anticiper dans un test unitaire. Pour rester prévisible, un test doit fonctionner indépendamment de l’environnement.&lt;/p&gt;

&lt;p&gt;Imaginons un cas d'activation / désactivation de fonctionnalité basé sur la valeur d'une variable dans l'environnement. Comment rendre le contrôle et la maitrise au système et permettre sa testabilité ?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Inversion de la dépendance entre le système et le collaborateur réel&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;EnvironmentProvider&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Implémentation réelle injectée par défaut dans le système qui récupère une variable d'environnement&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SystemEnvironmentProvider&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;EnvironmentProvider&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getenv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Implémentation contrôlable, injecté depuis les tests. &lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StubEnvironmentProvider&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;EnvironmentProvider&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;StubEnvironmentProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  L’état partagé mutable
&lt;/h4&gt;

&lt;p&gt;Un système devient instable lorsqu’il s’appuie sur un &lt;strong&gt;état partagé global et mutable&lt;/strong&gt;, tel que :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;des &lt;strong&gt;variables &lt;code&gt;static&lt;/code&gt;&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;un &lt;strong&gt;cache en mémoire&lt;/strong&gt; commun à plusieurs tests,&lt;/li&gt;
&lt;li&gt;ou des &lt;strong&gt;ressources singleton&lt;/strong&gt; accessibles globalement.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cette approche rend les tests vulnérables aux effets de bord et aux interférences croisées. Un test peut influencer l’état d’un autre sans lien apparent, introduisant des pannes sporadiques difficiles à diagnostiquer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Une démarche méthodologique
&lt;/h2&gt;

&lt;p&gt;L'analyse de la collaboration doit précéder toute décision architecturale. Elle implique d'identifier les acteurs du système, de cartographier leurs interactions, puis d'évaluer pour chaque collaboration : "Est-ce que je contrôle ce comportement ?", "Est-il nécessaire de garder le contrôle ?" Ces questions simples permettent de catégoriser chaque collaboration selon son degré de contrôlabilité.&lt;/p&gt;

&lt;p&gt;Cette démarche permet d'éviter les écueils classiques : sur-ingénierie des zones volatiles par excès d'abstraction, ou sous-estimation de leur complexité. Elle conduit à une architecture adaptant les solutions techniques à la nature réelle des problèmes rencontrés.&lt;/p&gt;

&lt;p&gt;La maîtrise des collaborations, centrée sur la notion de contrôlabilité, constitue ainsi le préalable indispensable à toute réflexion architecturale cohérente et à une stratégie de test efficace.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>softwareengineering</category>
      <category>testing</category>
      <category>learning</category>
    </item>
    <item>
      <title>L'attaque des clones mutants : améliorez la fiabilité de vos tests à l'aide des tests de mutation</title>
      <dc:creator>Nicolas Gauthier</dc:creator>
      <pubDate>Tue, 26 Nov 2024 08:01:47 +0000</pubDate>
      <link>https://forem.com/onepoint/lattaque-des-clones-mutants-ameliorez-la-fiabilite-de-vos-tests-a-laide-des-tests-de-mutation-adk</link>
      <guid>https://forem.com/onepoint/lattaque-des-clones-mutants-ameliorez-la-fiabilite-de-vos-tests-a-laide-des-tests-de-mutation-adk</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Il y a bien longtemps, dans une galaxie lointaine, très lointaine…&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Une sombre menace plane sur la planète Quality. Des clones mutants, silencieux et perfides, se répandent dans le code, menaçant d’infiltrer chaque système pour semer le chaos et la destruction.&lt;/p&gt;

&lt;p&gt;Mais l’espoir n’est pas perdu. La courageuse princesse Leia rassemble ses troupes de développeurs et, armée de puissants tests, prépare une contre-attaque acharnée pour éradiquer ces mutants et sauver Quality de l’effondrement.&lt;/p&gt;

&lt;p&gt;Développeurs, soyez prêts. La planète Quality a besoin de vous ! Ensemble, nous apprendrons à nous défendre contre ces mutants et repousser l'obscurité qui menace notre code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Un peu de contexte...
&lt;/h2&gt;

&lt;p&gt;En tant que développeurs, on aime quand tout se passe comme prévu. Une pratique aujourd'hui indispensable de notre quotidien consiste à écrire des tests automatisés en amont ou en aval des développements. On peut justifier ces tests pour :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Détecter les bugs&lt;/strong&gt; au plus tôt en cas d'évolution, et vérifier la &lt;strong&gt;non-régression&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spécifier de nouvelles exigences&lt;/strong&gt; et vérifier que le code fonctionne comme attendu,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gagner du temps&lt;/strong&gt; lors de la phase de tests manuels,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gagner du temps&lt;/strong&gt; lors des phases de développement futures et faciliter le &lt;strong&gt;refactoring&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documenter&lt;/strong&gt; le code,&lt;/li&gt;
&lt;li&gt;Ou encore, &lt;strong&gt;guider&lt;/strong&gt; le développeur &lt;strong&gt;explorateur&lt;/strong&gt; dans le &lt;strong&gt;design&lt;/strong&gt; de son code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Les tests, c'est bien. Encore faut-il qu'ils soient &lt;strong&gt;pertinents&lt;/strong&gt;, &lt;strong&gt;robustes&lt;/strong&gt; et suffisamment &lt;strong&gt;exhaustifs&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Modifier le code en toute confiance, tester te permettra ; et en production le vendredi, sereinement déployer, tu pourras.&lt;/p&gt;

&lt;p&gt;— &lt;cite&gt;Yoda (Grand maître développeur)&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Les tests de mutation, qu'est-ce que c'est ?
&lt;/h2&gt;

&lt;p&gt;Revenons au sujet de cet article : &lt;strong&gt;Les tests de mutation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Les &lt;strong&gt;tests de mutation&lt;/strong&gt;, c'est un peu comme un jeu d'attrape-moi-si-tu-peux dans le code. L'idée est d'y introduire volontairement de petits changements affectant le comportement du code, qu'on appelle &lt;strong&gt;mutants&lt;/strong&gt;, et de vérifier si les tests existants sont capables de les détecter.&lt;/p&gt;

&lt;p&gt;Comment sont générés ces mutants ? Généralement, les outils permettant de les générer travaillent directement au niveau du bytecode pour appliquer des modifications, par exemple :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changer des opérateurs arithmétiques,&lt;/li&gt;
&lt;li&gt;Altérer des opérateurs de comparaison,&lt;/li&gt;
&lt;li&gt;Modifier des constantes,&lt;/li&gt;
&lt;li&gt;Retirer des blocs de code,&lt;/li&gt;
&lt;li&gt;Modifier des valeurs de retour,&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ensuite, l'&lt;strong&gt;objectif&lt;/strong&gt; est simple : les &lt;strong&gt;tests&lt;/strong&gt; doivent &lt;strong&gt;tuer les mutants&lt;/strong&gt;.&lt;br&gt;
Pour tuer un mutant, au moins un des tests doit &lt;strong&gt;échouer&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;La Force est avec moi ! Mais n'est-ce pas créer un déséquilibre que de pousser nos tests à l'échec ?&lt;/p&gt;

&lt;p&gt;— &lt;cite&gt;Luke Skywalker (Développeur innocent)&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;



&lt;blockquote&gt;
&lt;p&gt;Luke, je sens la peur t'envahir, laisse-toi aller vers le côté obscur et ensemble nous...&lt;/p&gt;

&lt;p&gt;— &lt;cite&gt;Darth Vader (Développeur malveillant)&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;



&lt;blockquote&gt;
&lt;p&gt;Voooous ne passereeeez paaaaaaaaaas ! kof kof ... kof...&lt;/p&gt;

&lt;p&gt;— &lt;cite&gt;Gandalf (Développeur itinérant)&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Les limites de la couverture de code
&lt;/h3&gt;

&lt;p&gt;Quand on évoque la notion de &lt;strong&gt;couverture de code&lt;/strong&gt;, on pense généralement qu'il s'agit de la part de code traversé par l'exécution des tests automatisés. Cette métrique est très souvent mise en œuvre pour justifier d'une certaine &lt;strong&gt;qualité du code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Souvent, elle est imposée au niveau d'une entreprise sans en considérer les objectifs sous-jacents.&lt;br&gt;
Il n'est pas rare d'exiger un taux de couverture de 80%, 90%... Objectif qui peut être, selon le contexte, facilement atteignable. On parle alors d'&lt;a href="https://martinfowler.com/bliki/AssertionFreeTesting.html" rel="noopener noreferrer"&gt;Assertion Free Testing&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Chewie, tu entends ? Nous n'avons qu'à traverser, alors traversons !&lt;/p&gt;

&lt;p&gt;— &lt;cite&gt;Han Solo (Développeur fougueux)&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;



&lt;blockquote&gt;
&lt;p&gt;Rawrgwawgrr !&lt;/p&gt;

&lt;p&gt;— &lt;cite&gt;Chewbacca (Développeur d'accord)&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;En changeant notre point de vue sur la couverture de code, on parvient à rendre nos tests plus pertinents. Les tests de mutations sont un outil supplémentaire pour parvenir à cet objectif, qui vont nous permettre de révéler les défaillances de la couverture de code et donc de nos tests. En plus de la couverture induite par l'exécution, ils se concentrent sur la vérification des assertions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;La couverture doit être une conséquence et non un objectif, concentre ta force là où elle te permettra d'aller plus loin.&lt;/p&gt;

&lt;p&gt;— &lt;cite&gt;Obi-Wan Kenobi (Développeur sage)&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Les tests de mutation par l'exemple
&lt;/h2&gt;

&lt;p&gt;La théorie, c'est nécessaire, mais la pratique, c'est indispensable pour se forger un avis.&lt;/p&gt;

&lt;p&gt;Dans la suite de cet article, nous allons mettre en place ce type de tests dans un projet Java qui me sert de base à moultes expérimentations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Je suis prête à tous les affronter. Vitesse lumière !&lt;/p&gt;

&lt;p&gt;— &lt;cite&gt;Rey (Développeuse curieuse... et fougueuse aussi)&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  PITest : des mutants au sein de mes tests Java
&lt;/h3&gt;

&lt;p&gt;De nombreux outils de mutation testing existent selon les langages : &lt;a href="https://stryker-mutator.io/docs/" rel="noopener noreferrer"&gt;Stryker&lt;/a&gt; pour &lt;code&gt;JavaScript / TypeScript&lt;/code&gt;, &lt;a href="https://mutmut.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;mutmut&lt;/a&gt; pour le langage &lt;code&gt;python&lt;/code&gt;... Il en est un dans l'écosystème &lt;code&gt;Java&lt;/code&gt; (et JVMs associées) très populaire : &lt;a href="http://pitest.org/" rel="noopener noreferrer"&gt;PITest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Sa configuration avec un plugin maven est relativement simple.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ&lt;br&gt;
Pour plus d'informations sur les possibilités de configuration du plugin, je vous recommande d'aller voir la documentation existante : &lt;a href="https://pitest.org/quickstart/maven/" rel="noopener noreferrer"&gt;PITest : Maven Quick Start&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
  ...
  &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.pitest&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;pitest-maven&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;                       #1
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${pitest-maven.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.pitest&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;pitest-junit5-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;           #2
        &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${pitest-junit5-plugin.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;outputFormats&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;outputFormat&amp;gt;&lt;/span&gt;HTML&lt;span class="nt"&gt;&amp;lt;/outputFormat&amp;gt;&lt;/span&gt;                       #3
      &lt;span class="nt"&gt;&amp;lt;/outputFormats&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;targetClasses&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;targetClass&amp;gt;&lt;/span&gt;n.g.a.games.playhaa.games.**.domain.**.*&lt;span class="nt"&gt;&amp;lt;/targetClass&amp;gt;&lt;/span&gt;   #4
      &lt;span class="nt"&gt;&amp;lt;/targetClasses&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;targetTests&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;targetTest&amp;gt;&lt;/span&gt;n.g.a.games.playhaa.games.**.domain.**.*Test&lt;span class="nt"&gt;&amp;lt;/targetTest&amp;gt;&lt;/span&gt; #5
      &lt;span class="nt"&gt;&amp;lt;/targetTests&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;#1&lt;/strong&gt; - Le plugin maven pour PITest&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#2&lt;/strong&gt; - Le plugin de support pour les tests écrits avec JUnit 5&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#3&lt;/strong&gt; - Le format de sortie : Par défaut, c'est HTML (mais c'est pour vous dire que ça existe...)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#4&lt;/strong&gt; - Le filtre de sélection des classes à muter&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#5&lt;/strong&gt; - Le filtre de sélection des tests à exécuter avec les mutations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maintenant que vous avez intégré PITest à votre projet, il ne vous reste plus qu'à lancer votre premier test de mutation à l'aide de la commande suivante :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn clean test-compile org.pitest:pitest-maven:mutationCoverage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Les mutants ont-ils survécu ? Analyse des résultats de l'exécution de PITest
&lt;/h3&gt;

&lt;p&gt;À la suite de l'exécution des tests, un rapport HTML a été généré dans le répertoire &lt;code&gt;target/pit-reports&lt;/code&gt;.&lt;br&gt;
Ouvrons le fichier &lt;code&gt;index.html&lt;/code&gt; et analysons un peu les résultats.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodw27yx78i1k6dufrbo7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodw27yx78i1k6dufrbo7.png" alt="Rapport global" width="800" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tout d'abord, on a un résumé des résultats globaux :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Number of classes&lt;/strong&gt; : C'est le nombre total de classes qui ont muté dans le projet (correspondant au filtre &lt;code&gt;targetClasses&lt;/code&gt;),&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Line Coverage&lt;/strong&gt; : C'est le taux de couverture du code par les tests (exécution),&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutation Coverage&lt;/strong&gt; : C'est le ratio entre le nombre de mutants tués par les tests et le nombre de mutants générés,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test Strength&lt;/strong&gt; : C'est le ratio entre le nombre de mutants tués parmi tous les mutants couverts par les tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ces résultats globaux sont suivis d'une synthèse des résultats par package. Pour chaque package, il est possible d'aller consulter un rapport plus détaillé :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vyuzxcxlmow0cowpr9t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vyuzxcxlmow0cowpr9t.png" alt="Rapport - Vue par package" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si l'on souhaite obtenir le détail sur les mutations qui ont été réalisées, il est également possible d'accéder au rapport de chacune des classes qui ont été mutées :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdcnk6vied01t2unq37sf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdcnk6vied01t2unq37sf.png" alt="Rapport - Vue par classe (partie 1)" width="664" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7tuxk1xmj1q1irphw193.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7tuxk1xmj1q1irphw193.png" alt="Rapport - Vue par classe (partie 2)" width="681" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;On voit que ce n'est pas fait en TDD, maître...&lt;/p&gt;

&lt;p&gt;— &lt;cite&gt;C3PO (Développeur rabat-joie)&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Que constate-t-on ?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dans le package &lt;code&gt;n.g.a.games.playhaa.games.join.domain&lt;/code&gt;, le taux de couverture de code est de 100%.&lt;/li&gt;
&lt;li&gt;... Par conséquent, la couverture pour la classe &lt;code&gt;JoinGameService&lt;/code&gt; est également de 100%.&lt;/li&gt;
&lt;li&gt;Toutefois, dans cette classe, 3 mutants ont survécu, car non détectés par les tests :

&lt;ul&gt;
&lt;li&gt;À la ligne &lt;code&gt;43&lt;/code&gt;, l'addition a été remplacée par une soustraction&lt;/li&gt;
&lt;li&gt;À la ligne &lt;code&gt;44&lt;/code&gt;, la condition a été inversée&lt;/li&gt;
&lt;li&gt;À la ligne &lt;code&gt;45&lt;/code&gt;, la condition a également été inversée&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pourquoi de telles mutations qui semblent absolument non pertinentes ? Parce que le code évolue, les développeurs changent, le code devient de plus en plus riche et parfois complexe. L'introduction d'un changement sans en mesurer la portée peut avoir des effets de bords non désirés et provoquer des bugs.&lt;/p&gt;

&lt;p&gt;Ces mutations représentent la réalité de ce qui peut arriver dans la vie d'un logiciel.&lt;/p&gt;

&lt;p&gt;Si ces mutants ont survécu aux tests, c'est qu'aucun test n'a échoué suite à leur introduction. En résumé, il manque au moins une assertion.&lt;/p&gt;

&lt;p&gt;L'explication du taux de couverture de 100% se trouve dans un test écrit pour le cas nominal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@DisplayName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Should join an existing game"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;shouldJoinAnExistingGame&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Arrange&lt;/span&gt;
    &lt;span class="nc"&gt;GameId&lt;/span&gt; &lt;span class="n"&gt;joinedGameId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GameId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2a8fb592-876f-4f38-a014-3df9e810b1b0"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// ... prepare existing joinable game&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;maxPlayers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;DeckId&lt;/span&gt; &lt;span class="n"&gt;creatorDeckId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DeckId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"3a8fb592-876f-4f38-a014-3df9e810b1b0"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;Deck&lt;/span&gt; &lt;span class="n"&gt;creatorDeck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TestDeckBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;defaultBuilder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creatorDeckId&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;GamePlayer&lt;/span&gt; &lt;span class="n"&gt;currentCreatorPlayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TestGamePlayerBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;defaultBuilder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;deck&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creatorDeck&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="nc"&gt;Game&lt;/span&gt; &lt;span class="n"&gt;existingGame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Game&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;joinedGameId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"L'attaque des clones"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Difficulty&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STANDARD&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;GameStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;WAITING&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentCreatorPlayer&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="n"&gt;maxPlayers&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// #1&lt;/span&gt;

    &lt;span class="c1"&gt;// ... prepare command&lt;/span&gt;
    &lt;span class="nc"&gt;DeckId&lt;/span&gt; &lt;span class="n"&gt;joinerDeckId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DeckId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"73fa2619-5838-40e6-9aa9-8a341db3c418"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;JoinGameCommand&lt;/span&gt; &lt;span class="n"&gt;joinGameCommand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JoinGameCommand&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;joinedGameId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;joinerDeckId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// #2&lt;/span&gt;

    &lt;span class="c1"&gt;// ... prepare use case&lt;/span&gt;
    &lt;span class="nc"&gt;JoinGameRepository&lt;/span&gt; &lt;span class="n"&gt;joinGameRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InMemoryJoinGameRepository&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;existingGame&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;JoinGameUseCase&lt;/span&gt; &lt;span class="n"&gt;useCase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JoinGameService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;joinGameRepository&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Act&lt;/span&gt;
    &lt;span class="nc"&gt;Game&lt;/span&gt; &lt;span class="n"&gt;gameUpdated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;useCase&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;joinGameCommand&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// #3&lt;/span&gt;

    &lt;span class="c1"&gt;// Assert&lt;/span&gt;
    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;joinedGameId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gameUpdated&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;assertTrue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameUpdated&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasPlayerDeck&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;joinerDeckId&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// #4&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;#1&lt;/strong&gt; - On simule une partie existante avec un seul joueur (le créateur) et un nombre de joueurs maximum de 2.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#2&lt;/strong&gt; - On initie une commande avec l'id de la partie à rejoindre et l'id du deck du nouveau joueur&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#3&lt;/strong&gt; - On appelle la méthode &lt;code&gt;join&lt;/code&gt; du service lié à notre cas d'utilisation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#4&lt;/strong&gt; - On vérifie que le deck du nouveau joueur est bien présent dans la partie&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;L'objectif de ce test est de vérifier qu'un nouveau joueur qui rejoint une partie s'y retrouve bien. En aucun cas, on ne vérifie l'état de la partie, ce n'est pas l'objectif ici.&lt;/p&gt;

&lt;p&gt;En analysant davantage les tests existants de cette classe, on constate qu'aucun d'entre eux ne vient vérifier un changement sur l'état d'une partie suite à l'arrivée du dernier joueur et c'est la cause de la survie de nos chers mutants !&lt;/p&gt;

&lt;p&gt;Ajoutons donc un nouveau test avec l'assertion sur l'état du jeu :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@DisplayName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Should mark game as READY when max players is reached"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;shouldMarkGameAsReadyWhenMaxPlayersIsReached&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// ... les conditions d'entrée sont identiques au test du cas nominal précédent&lt;/span&gt;

    &lt;span class="c1"&gt;// Assert&lt;/span&gt;
    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GameStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;READY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gameUpdated&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Maintenant que nous avons ajouté ce nouveau test, relançons nos tests de mutation et vérifions les résultats obtenus :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn711fvtvz9hg1fxca8yc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn711fvtvz9hg1fxca8yc.png" alt="Rapport - Vue après correction" width="757" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Les mutants introduits dans notre classe ont tous été éliminés !&lt;/p&gt;

&lt;p&gt;Un peu d'explications :&lt;/p&gt;

&lt;p&gt;Lors de l'exécution du test du cas nominal vu précédemment - sans mutation -, la partie passe bien à l'état READY. Cependant, aucune assertion ne permettait de s'en assurer. Nos 3 mutants ont alors saisi cette occasion pour s'introduire discrètement dans notre classe et passer inaperçus.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nos efforts pour consolider les tests ont porté leurs fruits. Grâce à votre détermination, nous avons renforcé nos défenses et remporté une première bataille.&lt;/p&gt;

&lt;p&gt;Que la force soit avec nous !&lt;/p&gt;

&lt;p&gt;— &lt;cite&gt;Princesse Leia Organa (Lead Développeuse motivante)&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Les limites des tests de mutation
&lt;/h2&gt;

&lt;p&gt;Parlons maintenant des &lt;strong&gt;limites&lt;/strong&gt; de ces tests, car il y en a bien sûr ! On peut citer principalement :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Le &lt;strong&gt;temps d'exécution&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;La &lt;strong&gt;consommation en ressources&lt;/strong&gt; machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;L’un des coûts principaux de la MT est les ressources computationnelles nécessaires pour exécuter les tests. Cette technique implique la génération d’un grand nombre de mutants, chacun représentant une version légèrement modifiée du logiciel original, et l’exécution de la suite de test contre chacun de ces mutants. À mesure que le nombre de mutants augmente, la quantité de puissance de calcul et le temps d’exécution requis pour le processus de test augmentent de manière exponentielle.&lt;/p&gt;

&lt;p&gt;— &lt;cite&gt;Moez Krichen (&lt;a href="https://hal.science/hal-04098847/" rel="noopener noreferrer"&gt;Une enquête sur le test de mutation&lt;/a&gt;)&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Concentrons-nous sur la limite de temps d'exécution. Dans le cas des tests de mutation, ce temps est fonction de plusieurs paramètres, par exemple :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Les caractéristiques de la machine qui exécute les tests (processeur, mémoire, etc.),&lt;/li&gt;
&lt;li&gt;Le nombre de classes à muter et donc le nombre de mutants générés,&lt;/li&gt;
&lt;li&gt;Le nombre de tests sélectionnés et leur temps d'exécution dans un contexte normal (sans mutations),&lt;/li&gt;
&lt;li&gt;Les optimisations apportées telles que la parallélisation, les types de mutants générés,&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essayons de faire quelques tests dans le projet qui nous sert en exemple dans cet article.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Les résultats suivants sont obtenus sur un environnement de développement local.
Ils sont exécutés sur un laptop possédant un processeur Core i7 (1.80Ghz / 4 cœurs) et 16Go de RAM.&lt;/li&gt;
&lt;li&gt;Le terme &lt;strong&gt;domaine&lt;/strong&gt;, employé par la suite, correspond au cœur de l'application. Il est préservé de toute dépendance extérieure au projet (librairies, frameworks, etc.) et ne dépend pas de ce qui l'entoure (controllers HTTP, repositories JPA, etc.). On y retrouve uniquement des &lt;strong&gt;tests unitaires&lt;/strong&gt; (qui doivent-être rapides à s'exécuter). À l'extérieur du &lt;strong&gt;domaine&lt;/strong&gt;, on retrouvera des tests unitaires, mais aussi des &lt;strong&gt;tests d'intégration&lt;/strong&gt; (qui sont plus longs à s'exécuter).&lt;/li&gt;
&lt;li&gt;Le nombre de threads utilisés pour paralléliser l'exécution des tests de mutation avec PITest est de 4,&lt;/li&gt;
&lt;li&gt;Enfin, aucune optimisation supplémentaire n'a été réalisée lors de ces tests. L'objectif est ici, d'obtenir de premiers résultats et d'en sortir quelques hypothèses.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Contexte&lt;/th&gt;
&lt;th&gt;Le domaine "games"&lt;/th&gt;
&lt;th&gt;Tous les domaines&lt;/th&gt;
&lt;th&gt;Tout le projet&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nombre de classes sélectionnées&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;66&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nombre de mutants générés&lt;/td&gt;
&lt;td&gt;84&lt;/td&gt;
&lt;td&gt;167&lt;/td&gt;
&lt;td&gt;499&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nombre de tests sélectionnés&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;62&lt;/td&gt;
&lt;td&gt;131 &lt;strong&gt;#1&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nombre d'exécutions de tests&lt;/td&gt;
&lt;td&gt;111&lt;/td&gt;
&lt;td&gt;218&lt;/td&gt;
&lt;td&gt;855&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Temps d'exécution des tests de mutation &lt;strong&gt;#2&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;26 sec&lt;/td&gt;
&lt;td&gt;54 sec&lt;/td&gt;
&lt;td&gt;11 min 11 sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Temps d'exécution de référence des tests sélectionnés &lt;strong&gt;#3&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;982 ms&lt;/td&gt;
&lt;td&gt;1 sec 673 ms&lt;/td&gt;
&lt;td&gt;14 sec 72 ms &lt;strong&gt;#4&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;#1&lt;/strong&gt; - Le périmètre projet ici inclut des tests d'intégration qui sont plus longs à s'exécuter&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#2&lt;/strong&gt; - Temps nécessaire à l'exécution parallèle des tests de mutation,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#3&lt;/strong&gt; - Temps nécessaire à l'exécution des tests sélectionnés, sans mutations et sans parallélisation,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#4&lt;/strong&gt; - Dont 11 secondes pour les tests d'intégration nécessitant le démarrage de l'application context.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On peut d'ores et déjà constater que ces paramètres ont eu un impact significatif sur les temps d'exécution :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Le nombre de classes en entrée (et donc le nombre de mutants générés)&lt;/li&gt;
&lt;li&gt;Le temps d'exécution trop long de certains tests (ici les tests d'intégration)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Que peut-on en conclure ?
&lt;/h2&gt;

&lt;p&gt;Les avantages d'intégrer les tests de mutation dans notre processus de développement logiciel sont réels, nous avons pu le voir. Ils nous permettent d'&lt;strong&gt;identifier les lacunes et les faiblesses dans nos tests&lt;/strong&gt; dans l'optique de les consolider.&lt;/p&gt;

&lt;p&gt;Les projets vivent, les développeurs changent, ainsi que les pratiques de test. Ces mutants seront alors de précieux alliés qui permettront de maintenir un niveau de qualité et de fiabilité dans le temps.&lt;/p&gt;

&lt;p&gt;Toutefois, il faut également considérer leurs limites, dont les principales : le &lt;strong&gt;temps d'exécution&lt;/strong&gt; global pour l'ensemble des mutants générés, et la &lt;strong&gt;consommation en ressources&lt;/strong&gt; de calcul. Pour contourner ces limites, il conviendra alors de faire les meilleurs choix possibles en fonction de votre contexte.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tout ceci est bien joli maître, mais si cela avait été fait en TDD, sans doute que...&lt;/p&gt;

&lt;p&gt;Maître, que faites-vous ... non ne touchez (...)&lt;/p&gt;

&lt;p&gt;— &lt;cite&gt;C3PO (Développeur rabat-joie une fois de trop)&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Et maintenant... c'est à vous de jouer ;)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 En complément, on peut aussi reconsidérer un peu sa manière de concevoir et de tester. Il est une pratique qui, si elle est maitrisée, permet de grandement limiter ces défaillances : le TDD (Test Driven Development), mais ça, c'est une autre histoire...&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>testing</category>
      <category>development</category>
      <category>java</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
