<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Testing on kenji.blog</title><link>http://kenji.blog/fr/categories/testing/</link><description>Recent content in Testing on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>fr</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 21:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/fr/categories/testing/index.xml" rel="self" type="application/rss+xml"/><item><title>Comment introduire le développement dirigé par les tests (TDD) dans un projet C++ (GoogleTest, etc.)</title><link>http://kenji.blog/fr/p/tdd-cpp-googletest-introduction/</link><pubDate>Sat, 12 Sep 2026 21:00:00 +0900</pubDate><guid>http://kenji.blog/fr/p/tdd-cpp-googletest-introduction/</guid><description>&lt;img src="http://kenji.blog/p/tdd-cpp-googletest-introduction/img/eyecatch.jpg" alt="Featured image of post Comment introduire le développement dirigé par les tests (TDD) dans un projet C++ (GoogleTest, etc.)" />&lt;p>Dans le développement de logiciels modernes, ajouter rapidement des fonctionnalités tout en maintenant la qualité du code est une nécessité absolue. En particulier dans des langages complexes et exigeants en performances comme le C++, les erreurs de gestion de mémoire ou les comportements indéfinis (Undefined Behavior) conduisent facilement à des bugs fatals, ce qui rend l&amp;rsquo;importance des tests encore plus grande que dans d&amp;rsquo;autres langages.&lt;/p>
&lt;p>Dans cet article, nous expliquerons de manière très détaillée et pratique comment introduire le &lt;strong>développement dirigé par les tests (Test-Driven Development : TDD)&lt;/strong> dans un projet C++. Nous aborderons de manière exhaustive l&amp;rsquo;utilisation du framework de tests unitaires &lt;strong>GoogleTest&lt;/strong> et du framework de mock &lt;strong>GoogleMock&lt;/strong>, ainsi que la méthode de configuration moderne avec le système de build &lt;strong>CMake&lt;/strong>, et comment mesurer la couverture de code.&lt;/p>
&lt;h2 id="1-la-philosophie-et-les-avantages-du-développement-dirigé-par-les-tests-tdd">1. La philosophie et les avantages du développement dirigé par les tests (TDD)
&lt;/h2>&lt;p>Le développement dirigé par les tests (TDD) est une méthode de développement logiciel qui consiste à « écrire des tests avant d&amp;rsquo;écrire l&amp;rsquo;implémentation ». Cela ne fonctionne pas seulement comme une méthode de test, mais aussi comme une &lt;strong>méthode de conception&lt;/strong>. En écrivant les tests d&amp;rsquo;abord, les développeurs prennent naturellement conscience de créer une « interface facile à utiliser » et une « conception faiblement couplée ».&lt;/p>
&lt;h3 id="11-le-cycle-red-green-refactor">1.1 Le cycle Red-Green-Refactor
&lt;/h3>&lt;p>Au cœur du TDD se trouve le cycle suivant : « Red-Green-Refactor ».&lt;/p>
&lt;pre class="mermaid">
flowchart TD
Start[&amp;#34;Début du développement&amp;#34;] --&amp;gt; Red[&amp;#34;Red : Écrire un test qui échoue&amp;#34;]
Red --&amp;gt; Green[&amp;#34;Green : Faire l&amp;#39;implémentation minimale pour que le test réussisse&amp;#34;]
Green --&amp;gt; Refactor[&amp;#34;Refactor : Améliorer le code (Refactoring)&amp;#34;]
Refactor --&amp;gt; Red
Refactor -.-&amp;gt; End[&amp;#34;Fonctionnalité terminée&amp;#34;]
&lt;/pre>
&lt;ol>
&lt;li>&lt;strong>Red (Rouge)&lt;/strong> : En l&amp;rsquo;absence d&amp;rsquo;implémentation, écrire un test qui définit le comportement attendu. À ce stade, comme il n&amp;rsquo;y a pas d&amp;rsquo;implémentation, le test échoue toujours (Red).&lt;/li>
&lt;li>&lt;strong>Green (Vert)&lt;/strong> : Écrire le code minimal nécessaire uniquement pour faire réussir le test (Green). À cette étape, la beauté du code ou les performances ne sont pas la priorité absolue.&lt;/li>
&lt;li>&lt;strong>Refactor (Refactoring / Remaniement)&lt;/strong> : Tout en maintenant le test au vert, éliminer les duplications et améliorer la conception du code. La présence des tests permet de modifier le code en toute sécurité.&lt;/li>
&lt;/ol>
&lt;h3 id="12-laugmentation-des-coûts-due-au-retard-dans-la-découverte-des-bugs">1.2 L&amp;rsquo;augmentation des coûts due au retard dans la découverte des bugs
&lt;/h3>&lt;p>En génie logiciel, il est bien connu que plus la découverte d&amp;rsquo;un bug se produit tard dans le processus de développement, plus son coût de correction augmente de manière exponentielle. Ce modèle d&amp;rsquo;augmentation des coûts est parfois approximé par la formule mathématique suivante :&lt;/p>
$$ Cost(t) = C_0 \times e^{k \cdot t} $$&lt;p>Ici, $Cost(t)$ est le coût de correction au temps $t$, $C_0$ est le coût de correction (ligne de base) immédiatement après l&amp;rsquo;introduction du bug, et $k$ est une constante. L&amp;rsquo;introduction du TDD permet de maintenir $t$ à un niveau minimal, empêchant ainsi l&amp;rsquo;augmentation exponentielle des coûts.&lt;/p>
&lt;h2 id="2-le-choix-des-outils-de-test-en-c-et-la-configuration-moderne-avec-cmake">2. Le choix des outils de test en C++ et la configuration moderne avec CMake
&lt;/h2>&lt;p>Il existe de nombreux frameworks de test en C++, tels que Catch2, Boost.Test, doctest, etc., mais le plus largement utilisé comme standard de l&amp;rsquo;industrie est &lt;strong>GoogleTest (gtest)&lt;/strong>. GoogleTest séduit par la richesse de ses assertions, son puissant framework de mock (GoogleMock) et sa grande extensibilité.&lt;/p>
&lt;h3 id="21-introduction-de-googletest-via-fetchcontent-de-cmake">2.1 Introduction de GoogleTest via &lt;code>FetchContent&lt;/code> de CMake
&lt;/h3>&lt;p>Dans le développement C++ moderne, la gestion des dépendances externes se fait couramment en utilisant le module &lt;code>FetchContent&lt;/code> de CMake. Cela permet d&amp;rsquo;éviter la gestion des sous-modules ou l&amp;rsquo;installation préalable de bibliothèques.&lt;/p>
&lt;p>Le fichier &lt;code>CMakeLists.txt&lt;/code> à la racine du projet s&amp;rsquo;écrit de la manière suivante :&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cmake_minimum_required&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">VERSION&lt;/span> &lt;span class="s">3.14&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">TddCppExample&lt;/span> &lt;span class="s">CXX&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Spécification du standard C++
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_STANDARD&lt;/span> &lt;span class="s">17&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_STANDARD_REQUIRED&lt;/span> &lt;span class="s">ON&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Transformation du code de production en bibliothèque
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">core_lib&lt;/span> &lt;span class="s">src/Calculator.cpp&lt;/span> &lt;span class="s">src/StringUtils.cpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_include_directories&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">core_lib&lt;/span> &lt;span class="s">PUBLIC&lt;/span> &lt;span class="s">include&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Activation des tests
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">enable_testing&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Récupération de GoogleTest
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">include&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">FetchContent&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">FetchContent_Declare&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">googletest&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">URL&lt;/span> &lt;span class="s">https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Pour éviter les avertissements de compilation dans un environnement Windows
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">gtest_force_shared_crt&lt;/span> &lt;span class="s">ON&lt;/span> &lt;span class="s">CACHE&lt;/span> &lt;span class="s">BOOL&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span> &lt;span class="s">FORCE&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">FetchContent_MakeAvailable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">googletest&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Configuration de l&amp;#39;exécutable de test
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_executable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">unit_tests&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">tests/CalculatorTest.cpp&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">tests/StringUtilsTest.cpp&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">unit_tests&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">PRIVATE&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">core_lib&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">gtest_main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">gmock&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Enregistrement dans CTest
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">include&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">GoogleTest&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">gtest_discover_tests&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">unit_tests&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Grâce à cette configuration, CMake téléchargera automatiquement le code source de GoogleTest et l&amp;rsquo;intégrera au projet.&lt;/p>
&lt;h2 id="3-pratique--le-cycle-red-green-refactor-avec-googletest">3. Pratique : Le cycle Red-Green-Refactor avec GoogleTest
&lt;/h2>&lt;p>À partir d&amp;rsquo;ici, nous allons mettre en pratique le cycle TDD en prenant comme exemple une simple classe &lt;code>Calculator&lt;/code>.&lt;/p>
&lt;h3 id="31-phase-1--red-écrire-un-test-qui-échoue">3.1 Phase 1 : Red (Écrire un test qui échoue)
&lt;/h3>&lt;p>Tout d&amp;rsquo;abord, nous écrivons le squelette du fichier d&amp;rsquo;en-tête &lt;code>include/Calculator.h&lt;/code> et le code de test.&lt;/p>
&lt;p>&lt;strong>include/Calculator.h (Squelette)&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#pragma once
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Calculator&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>tests/CalculatorTest.cpp (Code de test)&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;gtest/gtest.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;#34;Calculator.h&amp;#34;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TEST&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">CalculatorTest&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">AddsTwoPositiveNumbers&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Calculator&lt;/span> &lt;span class="n">calc&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">calc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">EXPECT_EQ&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Si l&amp;rsquo;on essaie de compiler à ce stade, on obtiendra une erreur d&amp;rsquo;édition de liens (link) car l&amp;rsquo;implémentation de &lt;code>Calculator::Add&lt;/code> n&amp;rsquo;existe pas, ou si le test est exécuté, il échouera (Red).&lt;/p>
&lt;h3 id="32-phase-2--green-implémentation-minimale">3.2 Phase 2 : Green (Implémentation minimale)
&lt;/h3>&lt;p>Nous écrivons le code nécessaire uniquement pour faire réussir le test.&lt;/p>
&lt;p>&lt;strong>src/Calculator.cpp&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;#34;Calculator.h&amp;#34;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">Calculator&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// L&amp;#39;implémentation minimale pour passer le test
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>En compilant et en exécutant le test maintenant, le test réussira (Green).&lt;/p>
&lt;h3 id="33-phase-3--refactor-refactoring--remaniement">3.3 Phase 3 : Refactor (Refactoring / Remaniement)
&lt;/h3>&lt;p>Dans cet exemple, le code est très simple, mais à mesure que les exigences se complexifient, la phase de refactoring permet d&amp;rsquo;améliorer la lisibilité du code ou d&amp;rsquo;optimiser les performances. Le code de test lui-même est aussi sujet au refactoring. Par exemple, on peut envisager d&amp;rsquo;introduire des &amp;ldquo;fixtures&amp;rdquo; de test (&lt;code>testing::Test&lt;/code>) pour mutualiser la configuration.&lt;/p>
&lt;h2 id="4-différence-entre-expect_eq-et-assert_eq">4. Différence entre &lt;code>EXPECT_EQ&lt;/code> et &lt;code>ASSERT_EQ&lt;/code>
&lt;/h2>&lt;p>Lors de l&amp;rsquo;utilisation de GoogleTest, il existe deux types de macros d&amp;rsquo;assertion : &lt;code>EXPECT_*&lt;/code> et &lt;code>ASSERT_*&lt;/code>. Comprendre leurs différences est très important pour écrire des tests robustes.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>EXPECT_EQ(expected, actual)&lt;/code>&lt;/strong> : Même si le test échoue, l&amp;rsquo;exécution de la fonction de test actuelle &lt;strong>continue&lt;/strong>. C&amp;rsquo;est adapté lorsque l&amp;rsquo;on souhaite vérifier plusieurs états au sein d&amp;rsquo;un seul test.&lt;/li>
&lt;li>&lt;strong>&lt;code>ASSERT_EQ(expected, actual)&lt;/code>&lt;/strong> : Si le test échoue, l&amp;rsquo;exécution de la fonction de test actuelle est &lt;strong>interrompue (échec fatal)&lt;/strong> sur-le-champ. C&amp;rsquo;est utilisé lorsque les vérifications suivantes n&amp;rsquo;ont plus de sens (ex : déréférencer un pointeur juste après avoir vérifié qu&amp;rsquo;il n&amp;rsquo;est pas &lt;code>nullptr&lt;/code>).&lt;/li>
&lt;/ul>
&lt;h2 id="5-injection-de-dépendances-di-et-mocking-avec-googlemock">5. Injection de dépendances (DI) et Mocking avec GoogleMock
&lt;/h2>&lt;p>Dans un projet C++ réel, il y a toujours des dépendances envers des systèmes externes, comme l&amp;rsquo;accès aux bases de données, les communications réseau, le contrôle de matériel, etc. Si on laisse ces dépendances telles quelles, les tests unitaires deviennent très difficiles.&lt;/p>
&lt;p>C&amp;rsquo;est là qu&amp;rsquo;interviennent l&amp;rsquo;&lt;strong>injection de dépendances (Dependency Injection : DI)&lt;/strong> et le &amp;ldquo;mocking&amp;rdquo; d&amp;rsquo;interfaces avec &lt;strong>GoogleMock&lt;/strong>.&lt;/p>
&lt;pre class="mermaid">
flowchart LR
Test[&amp;#34;Test unitaire&amp;#34;] --&amp;gt;|injecte le mock| Target[&amp;#34;Service cible&amp;#34;]
Target --&amp;gt;|dépend de| Interface[&amp;#34;Interface (IUserRepository)&amp;#34;]
Mock[&amp;#34;MockUserRepository&amp;#34;] -.-&amp;gt;|implémente| Interface
Test --&amp;gt;|configure| Mock
&lt;/pre>
&lt;h3 id="51-définition-de-linterface-et-implémentation-de-la-classe-cible">5.1 Définition de l&amp;rsquo;interface et implémentation de la classe cible
&lt;/h3>&lt;p>Tout d&amp;rsquo;abord, nous définissons une interface (une classe avec des fonctions virtuelles pures) qui fait l&amp;rsquo;abstraction du composant dont on dépend.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// include/IUserRepository.h
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#pragma once
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;string&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">IUserRepository&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="o">~&lt;/span>&lt;span class="n">IUserRepository&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">default&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="nf">SaveUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Ensuite, nous créons la classe de service (cible du test) qui dépend de cette interface. Nous injectons la dépendance via le constructeur (Constructor Injection).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// include/UserService.h
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#pragma once
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;#34;IUserRepository.h&amp;#34;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;string&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">UserService&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">IUserRepository&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">repository_&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">UserService&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">IUserRepository&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">repository&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="n">repository_&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">repository&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">bool&lt;/span> &lt;span class="nf">RegisterUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">empty&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">repository_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SaveUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="52-création-de-la-classe-mock-avec-googlemock-et-test">5.2 Création de la classe mock avec GoogleMock et test
&lt;/h3>&lt;p>Nous utilisons la macro &lt;code>MOCK_METHOD&lt;/code> de GoogleMock pour créer le mock de l&amp;rsquo;interface.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// tests/UserServiceTest.cpp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;gtest/gtest.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;gmock/gmock.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;#34;UserService.h&amp;#34;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;#34;IUserRepository.h&amp;#34;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="o">::&lt;/span>&lt;span class="n">testing&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="o">::&lt;/span>&lt;span class="n">testing&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">_&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Définition de la classe mock
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">class&lt;/span> &lt;span class="nc">MockUserRepository&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="k">public&lt;/span> &lt;span class="n">IUserRepository&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MOCK_METHOD&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">bool&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">SaveUser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">override&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TEST&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">UserServiceTest&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">RegistersValidUserSuccessfully&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MockUserRepository&lt;/span> &lt;span class="n">mockRepo&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">UserService&lt;/span> &lt;span class="nf">service&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mockRepo&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Configuration de l&amp;#39;attente : on s&amp;#39;attend à ce que SaveUser soit appelée 1 fois avec (1, &amp;#34;Kenji&amp;#34;) et retourne true
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">EXPECT_CALL&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mockRepo&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">SaveUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Kenji&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">Times&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">WillOnce&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Return&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">true&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Exécution de la cible du test
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">service&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">RegisterUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Kenji&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Assertion
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">EXPECT_TRUE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TEST&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">UserServiceTest&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">RejectsEmptyNameWithoutCallingRepository&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MockUserRepository&lt;/span> &lt;span class="n">mockRepo&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">UserService&lt;/span> &lt;span class="nf">service&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mockRepo&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// On s&amp;#39;attend à ce que SaveUser ne soit jamais appelée dans le cas d&amp;#39;un nom vide
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">EXPECT_CALL&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mockRepo&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">SaveUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">_&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">Times&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">bool&lt;/span> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">service&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">RegisterUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">EXPECT_FALSE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>En utilisant GoogleMock de cette manière, on peut vérifier précisément « si la classe cible interagit correctement avec ses dépendances (interaction) ».&lt;/p>
&lt;h2 id="6-mesure-et-visualisation-de-la-couverture-de-code">6. Mesure et visualisation de la couverture de code
&lt;/h2>&lt;p>Après avoir écrit les tests, afin d&amp;rsquo;évaluer objectivement quelles parties du projet sont exécutées (couvertes) par les tests, on mesure la &lt;strong>couverture de code&lt;/strong>. La couverture de code ($Coverage$) est représentée par la formule suivante :&lt;/p>
$$ Coverage = \left( \frac{L_{executed}}{L_{total}} \right) \times 100 \ (\%) $$&lt;p>Ici, $L_{executed}$ est le nombre de lignes de code exécutées pendant les tests, et $L_{total}$ est le nombre total de lignes de code du projet.&lt;/p>
&lt;p>Si vous utilisez GCC ou Clang, vous pouvez mesurer la couverture à l&amp;rsquo;aide des outils &lt;code>gcov&lt;/code> et &lt;code>lcov&lt;/code>.&lt;/p>
&lt;h3 id="61-ajout-des-options-de-couverture-à-cmake">6.1 Ajout des options de couverture à CMake
&lt;/h3>&lt;p>Pour mesurer la couverture, des drapeaux (flags) de compilation spécifiques sont nécessaires. Ajoutez la configuration suivante à &lt;code>CMakeLists.txt&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Options de compilation pour la couverture
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">option&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ENABLE_COVERAGE&lt;/span> &lt;span class="s2">&amp;#34;Enable coverage reporting&amp;#34;&lt;/span> &lt;span class="s">OFF&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ENABLE_COVERAGE&lt;/span> &lt;span class="s">AND&lt;/span> &lt;span class="s">CMAKE_CXX_COMPILER_ID&lt;/span> &lt;span class="s">MATCHES&lt;/span> &lt;span class="s2">&amp;#34;GNU|Clang&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">message&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">STATUS&lt;/span> &lt;span class="s2">&amp;#34;Coverage enabled&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">target_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">core_lib&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">--coverage&lt;/span> &lt;span class="s">-O0&lt;/span> &lt;span class="s">-g&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">target_link_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">core_lib&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">--coverage&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">target_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">unit_tests&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">--coverage&lt;/span> &lt;span class="s">-O0&lt;/span> &lt;span class="s">-g&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">target_link_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">unit_tests&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">--coverage&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="62-procédure-de-génération-du-rapport-de-couverture">6.2 Procédure de génération du rapport de couverture
&lt;/h3>&lt;p>Activez le flag lors de la compilation, puis utilisez &lt;code>lcov&lt;/code> pour générer un rapport HTML après avoir exécuté les tests.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. Compiler en activant les options de couverture&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mkdir build &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake .. -DENABLE_COVERAGE&lt;span class="o">=&lt;/span>ON
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">make
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. Exécution des tests&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ctest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. Collecte des données de couverture (exécution de lcov)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">lcov --capture --directory . --output-file coverage.info
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. Exclusion des en-têtes du système et des bibliothèques externes (GoogleTest, etc.)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">lcov --remove coverage.info &lt;span class="s1">&amp;#39;/usr/*&amp;#39;&lt;/span> &lt;span class="s1">&amp;#39;*/_deps/*&amp;#39;&lt;/span> &lt;span class="s1">&amp;#39;*/tests/*&amp;#39;&lt;/span> --output-file coverage.info
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 5. Génération du rapport HTML&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">genhtml coverage.info --output-directory coverage_report
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>En ouvrant &lt;code>coverage_report/index.html&lt;/code> généré dans votre navigateur, les lignes exécutées seront visuellement mises en évidence en vert et en rouge au niveau du code source, ce qui sera utile pour identifier les omissions de tests (trous de couverture).&lt;/p>
&lt;h2 id="7-défis-du-tdd-dans-les-projets-c-et-meilleures-pratiques">7. Défis du TDD dans les projets C++ et meilleures pratiques
&lt;/h2>&lt;p>L&amp;rsquo;introduction du TDD dans les projets C++ présente des défis spécifiques.&lt;/p>
&lt;h3 id="71-augmentation-du-temps-de-build-temps-de-compilation">7.1 Augmentation du temps de build (temps de compilation)
&lt;/h3>&lt;p>En C++, les temps de compilation ont tendance à s&amp;rsquo;allonger en raison de l&amp;rsquo;utilisation intensive des templates et de l&amp;rsquo;inclusion massive d&amp;rsquo;en-têtes. Comme le cycle « Red-Green-Refactor » du TDD doit être effectué rapidement, un retard dans le temps de compilation est fatal.
&lt;strong>Solution&lt;/strong> : Minimisez les dépendances des fichiers d&amp;rsquo;en-tête en utilisant des déclarations anticipées (Forward Declarations) et l&amp;rsquo;idiome Pimpl (Pointer to implementation). L&amp;rsquo;introduction d&amp;rsquo;outils de cache de compilation tels que Ccache est également efficace.&lt;/p>
&lt;h3 id="72-introduction-du-tdd-sur-du-code-legacy-hérité">7.2 Introduction du TDD sur du code legacy (hérité)
&lt;/h3>&lt;p>Appliquer rétroactivement le TDD à un énorme code monolithique existant est extrêmement difficile.
&lt;strong>Solution&lt;/strong> : Plutôt que de tout réécrire depuis le début, il est recommandé d&amp;rsquo;adopter une approche consistant à ajouter des tests de manière incrémentale sur les parties où de nouvelles fonctionnalités sont ajoutées ou là où des bugs sont corrigés (la règle du Boy Scout), plaçant ainsi progressivement la base de code sous le contrôle du TDD (Méthode de &amp;ldquo;Working Effectively with Legacy Code&amp;rdquo;).&lt;/p>
&lt;h2 id="8-le-tdd-comme-conception-logicielle">8. Le TDD comme conception logicielle
&lt;/h2>&lt;p>Le TDD est un filet de sécurité pour maintenir la qualité du code, mais c&amp;rsquo;est aussi un moteur pour améliorer la conception du code C++. Étant donné que vous êtes contraint d&amp;rsquo;utiliser l&amp;rsquo;injection de dépendances (DI) pour pouvoir écrire des tests, le couplage (Coupling) entre les classes diminue et la cohésion (Cohesion) augmente.&lt;/p>
&lt;p>Lors du refactoring, il est également important de prêter attention à la complexité cyclomatique (Complexité cyclomatique de McCabe).&lt;/p>
$$ M = E - N + 2P $$&lt;p>($M$ : Complexité, $E$ : Nombre d&amp;rsquo;arêtes, $N$ : Nombre de nœuds, $P$ : Nombre de composantes connexes)&lt;/p>
&lt;p>L&amp;rsquo;existence de tests permet de diviser les fonctions ou de les remplacer par du polymorphisme afin de réduire cette complexité, sans craindre de casser le code existant.&lt;/p>
&lt;h2 id="résumé">Résumé
&lt;/h2>&lt;p>Dans cet article, nous avons expliqué en détail comment introduire le développement dirigé par les tests (TDD) à l&amp;rsquo;aide de GoogleTest et GoogleMock dans un projet C++.&lt;/p>
&lt;ol>
&lt;li>Configuration de projet moderne en utilisant &lt;strong>CMake FetchContent&lt;/strong>&lt;/li>
&lt;li>Pratique du cycle &lt;strong>Red-Green-Refactor&lt;/strong>&lt;/li>
&lt;li>Mocking d&amp;rsquo;interfaces en utilisant &lt;strong>GoogleMock et l&amp;rsquo;injection de dépendances (DI)&lt;/strong>&lt;/li>
&lt;li>Visualisation de la couverture de code par &lt;strong>gcov/lcov&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>Bien que le TDD soit une approche qui demande du temps pour être maîtrisée, le retour sur investissement est inestimable dans la programmation système où l&amp;rsquo;équilibre entre performances et sécurité est requis, comme en C++. Nous vous invitons à pratiquer progressivement le TDD dès votre prochain projet pour obtenir un code C++ robuste et facile à maintenir.&lt;/p></description></item></channel></rss>