<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="https://ktherage.github.io/fr/xsl/atom.xsl" media="all"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">
  <id>https://ktherage.github.io/fr/tags/testing/</id>
  <title>Kévin THÉRAGE | Expert Symfony Developer - Testing</title>
  <subtitle><![CDATA[Kévin THÉRAGE – Expert Symfony Developer. Technical blog on Symfony, PHP, web development with tutorials, best practices and expert advice for developers.]]></subtitle>
  <link href="https://ktherage.github.io/fr/tags/testing/atom.xml" rel="self" type="application/atom+xml" />
  <link href="https://ktherage.github.io/fr/tags/testing/" rel="alternate" type="text/html" />
  <updated>2026-06-08T09:33:13+00:00</updated>
  <author>
    <name>Kévin THÉRAGE</name>
    <uri>https://ktherage.github.io/</uri>
  </author>
  <entry xml:lang="fr">
    <id>https://ktherage.github.io/fr/blog/dealing_with_isolated_phpstan_1_and_the_phpunit_13_blindspot/</id>
    <title>Gérer l&#039;isolation de PHPStan 1 et l&#039;angle mort de PHPUnit 13</title>
    <published>2026-05-29T00:00:00+00:00</published>
    <link href="https://ktherage.github.io/fr/blog/dealing_with_isolated_phpstan_1_and_the_phpunit_13_blindspot/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<p>Nous adorons les outils de développement isolés. Placer PHPStan, Rector ou PHP CS Fixer dans des sous-répertoires distincts comme <code translate="no">.tools/phpstan/</code> avec leur propre <code translate="no">composer.json</code> est un excellent moyen d'éviter l'enfer des dépendances dans votre projet racine.</p>
<p>Jusqu'à ce que cela rende votre pipeline d'analyse complètement aveugle.</p>
<p>Si vous êtes récemment passé à <strong>PHPUnit 13</strong> et que votre analyse statique a soudainement déraillé avec des erreurs fantômes du type <code translate="no">unknown class PHPUnit\Framework\TestCase</code>, vous vous êtes heurté à un mur d'isolation classique. Voyons pourquoi cela ne fonctionne plus et comment corriger le tir proprement.</p>
<hr>
<h2 id="le-symptome">Le Symptôme</h2>
<p>Votre suite de tests s'exécute dans Docker. Tout passe haut la main. Chaque assertion est au vert.
Pourtant, dès que vous lancez PHPStan, votre terminal explose :</p>
<pre><code class="language-text" translate="no"> ------ ---------------------------------------------------------------------------- 
  Line   tests/Client/FakeClientTest.php                                             
 ------ ---------------------------------------------------------------------------- 
  12     Class App\Tests\Client\FakeClientTest extends unknown class                 
         PHPUnit\Framework\TestCase.                                                 
         💡 Learn more at https://phpstan.org/user-guide/discovering-symbols         
  30     Call to an undefined static method                                          
         App\Tests\Client\FakeClientTest::assertInstanceOf().                        
 ------ ---------------------------------------------------------------------------- </code></pre>
<p>Vous jetez un œil à votre fichier <code translate="no">phpstan.neon.dist</code>. Vous avez pourtant déjà fait le pont en indiquant à PHPStan où trouver l'autoloader du projet :</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">parameters:</span>
    <span class="hljs-attr">level:</span> <span class="hljs-string">max</span>
    <span class="hljs-attr">paths:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">src/</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">tests/</span>
    <span class="hljs-attr">bootstrapFiles:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">vendor/autoload.php</span></code></pre>
<p>Vous double-vérifiez même l'autoloader manuellement via PHP :</p>
<pre><code class="language-bash hljs bash" translate="no">php -r <span class="hljs-string">"require 'vendor/autoload.php'; echo class_exists('PHPUnit\Framework\TestCase') ? '🟢 OUI' : '🔴 NON';"</span></code></pre>
<p>La console renvoie <code translate="no">🟢 OUI</code>. La classe est bien là. Alors, pourquoi PHPStan est-il aveugle ?</p>
<hr>
<h2 id="pourquoi-cela-fonctionnait-il-tres-bien-avec-phpunit-10">Pourquoi cela fonctionnait-il très bien avec PHPUnit 10 ?</h2>
<p>Si vous avez exactement cette même configuration sur un projet plus ancien tournant sous PHPUnit 9.5, tout fonctionne sans accroc. Qu'est-ce qui a changé ?</p>
<h3 id="1-le-virage-architectural-de-phpunit-10">1. Le virage architectural de PHPUnit 10+</h3>
<p>Dans PHPUnit 9, <code translate="no">TestCase</code> était une classe plutôt monolithique. Le moteur de réflexion statique de PHPStan (<code translate="no">BetterReflection</code>) n'avait aucun mal à la cartographier depuis un répertoire externe.</p>
<p>Avec PHPUnit 10 (et jusqu'à la v13), le framework a été entièrement refactorisé. <code translate="no">TestCase</code> s'appuie désormais sur un réseau complexe d'interfaces et de traits internes. Lorsque PHPStan tente de l'inspecter à distance au-delà des frontières du répertoire via un autoloader bootstrappé, le moteur de réflexion se perd dans l'arbre d'héritage et considère prudemment que la classe n'existe pas.</p>
<h3 id="2-le-piege-de-l-extension-obsolete">2. Le piège de l'extension obsolète</h3>
<p>Si vous regardez le fichier <code translate="no">.tools/phpstan/composer.json</code> de votre outil isolé, vous y trouverez probablement une contrainte héritée d'un ancien boilerplate de projet :</p>
<pre><code class="language-json hljs json" translate="no"><span class="hljs-string">"require"</span>: {
    <span class="hljs-attr">"phpstan/phpstan"</span>: <span class="hljs-string">"*"</span>,
    <span class="hljs-attr">"phpstan/phpstan-phpunit"</span>: <span class="hljs-string">"^1.1"</span>
}</code></pre>
<p>Cette contrainte <code translate="no">^1.1</code> verrouille l'extension PHPUnit sur sa <strong>branche 1.x</strong>, historiquement conçue pour PHPUnit 9. L'extension étant bloquée en v1.x, Composer fige silencieusement le cœur de <code translate="no">phpstan/phpstan</code> dans une version obsolète elle aussi (comme la <code translate="no">1.12.x</code>), ignorant complètement votre wildcard <code translate="no">*</code>. Vous vous retrouvez concrètement à analyser du code moderne sous PHPUnit 13 avec un moteur daté.</p>
<hr>
<h2 id="la-solution-propre-abandonner-les-contraintes-obsoletes">La solution propre : abandonner les contraintes obsolètes</h2>
<p>Plutôt que de vous battre avec les chemins via <code translate="no">scanDirectories</code> ou d'installer une copie factice de PHPUnit dans le répertoire de vos outils, mettez simplement à jour votre chaîne d'outils. <strong>PHPStan 2.0</strong> et son extension <strong>phpstan-phpunit 2.0</strong> gèrent nativement l'architecture complexe du PHPUnit moderne.</p>
<h3 id="1-passer-a-la-v2">1. Passer à la v2</h3>
<p>Ouvrez <code translate="no">.tools/phpstan/composer.json</code> et forcez la mise à jour :</p>
<pre><code class="language-json hljs json" translate="no">{
    <span class="hljs-attr">"require"</span>: {
        <span class="hljs-attr">"php"</span>: <span class="hljs-string">"&gt;=8.4"</span>,
        <span class="hljs-attr">"phpstan/phpstan"</span>: <span class="hljs-string">"^2.0"</span>,
        <span class="hljs-attr">"phpstan/phpstan-phpunit"</span>: <span class="hljs-string">"^2.0"</span>
    },
    <span class="hljs-attr">"config"</span>: {
        <span class="hljs-attr">"bin-dir"</span>: <span class="hljs-string">"./"</span>,
        <span class="hljs-attr">"sort-packages"</span>: <span class="hljs-literal">true</span>
    }
}</code></pre>
<h3 id="2-rafraichir-l-environnement">2. Rafraîchir l'environnement</h3>
<p>Lancez une mise à jour dans le répertoire de votre outil pour reconstruire le fichier lock :</p>
<pre><code class="language-bash hljs bash" translate="no"><span class="hljs-built_in">cd</span> .tools/phpstan &amp;&amp; composer update</code></pre>
<h3 id="3-vider-le-cache-et-analyser">3. Vider le cache et analyser</h3>
<p>Assurez-vous que votre fichier <code translate="no">phpstan.neon.dist</code> utilise bien la variable de chemin absolu pour cibler le répertoire vendor racine de manière sécurisée :</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">parameters:</span>
    <span class="hljs-attr">level:</span> <span class="hljs-string">max</span>
    <span class="hljs-attr">paths:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">src/</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">tests/</span>
    <span class="hljs-attr">bootstrapFiles:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">%currentWorkingDirectory%/vendor/autoload.php</span>

<span class="hljs-attr">includes:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">.tools/phpstan/vendor/phpstan/phpstan-phpunit/extension.neon</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">.tools/phpstan/vendor/phpstan/phpstan-phpunit/rules.neon</span></code></pre>
<p>Supprimez l'ancien cache d'analyse pour éviter les résultats obsolètes :</p>
<pre><code class="language-bash hljs bash" translate="no">.tools/phpstan/phpstan clear-result-cache</code></pre>
<p>Relancez votre analyseur. Les erreurs fantômes vont disparaître et vous retrouverez vos indicateurs au vert, sans pour autant dégrader l'architecture isolée de vos outils.</p>]]>
    </content>
  </entry>
  <entry xml:lang="fr">
    <id>https://ktherage.github.io/fr/blog/bash-and-curl-simple-http-call-performance-testing/</id>
    <title>Bash &amp; Curl : Test de performance simple d&#039;appels HTTP</title>
    <published>2024-01-19T00:00:00+00:00</published>
    <link href="https://ktherage.github.io/fr/blog/bash-and-curl-simple-http-call-performance-testing/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<p>Les appels HTTP sont essentiels au fonctionnement du Web et sont critiques pour tout projet de développement web. Vous vous êtes peut-être demandé comment visualiser rapidement les performances de vos appels HTTP. Je vais vous montrer comment j'ai fait simplement avec Curl.</p>
<p><strong>TL;DR :</strong> Vous voulez juste le code (je comprends parfaitement 😉) ? Faites défiler jusqu'à l'extrait de code.</p>
<h1>Quel était mon besoin concernant les appels HTTP ?</h1>
<p>J'avais besoin d'avoir une idée rapide des performances du système de cache que j'avais mis en place sur un point de terminaison HTTP, et je voulais que ce soit rapide et simple.</p>
<p>Avec mes notions de Shell et ma connaissance de base de Curl (un petit cadeau d'un collègue : vous trouverez une antisèche Curl ici), je savais que je pouvais facilement exécuter cent fois le même appel HTTP et obtenir le temps total en résultat.</p>
<p>Cette solution est une solution courte et simple qui correspond à mes besoins, à savoir tester localement mon point de terminaison HTTP.
Si vous prévoyez de faire de véritables tests de performance/charge sur des serveurs réels comme la staging ou la production, alors jetez un œil à des outils comme <a href="https://jmeter.apache.org/" rel="noopener noreferrer">Apache JMeter</a>, <a href="https://gatling.io/" rel="noopener noreferrer">Gatling</a>, ou d'autres outils similaires.</p>
<h1>Comment Curl m'a aidé à tester mon appel HTTP ?</h1>
<p>J'ai donc ouvert mon terminal et exécuté :</p>
<pre><code class="language-bash hljs bash" translate="no"><span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> {1..100}; <span class="hljs-keyword">do</span> curl <span class="hljs-string">'https://some-domain/some-uri/some-path?cache=false'</span> \
-H <span class="hljs-string">'cache-control: no-cache'</span> \
-H <span class="hljs-string">'pragma: no-cache'</span> \
--compressed \
--insecure -s -o /dev/null -w <span class="hljs-string">"%{time_total}s\n"</span>;
<span class="hljs-keyword">done</span>

<span class="hljs-comment"># Which printed :</span>
0.057703s
0.067895s
0.063033s
0.062455s
0.074864s
...</code></pre>
<p>Pour quelques explications, j'ai copié la requête envoyée par mon navigateur sous forme de requête Curl (cela se fait facilement sur la plupart des navigateurs, voir <a href="https://quickref.me/curl" rel="noopener noreferrer">ici</a>) et je l'ai encapsulée dans une boucle <code translate="no">for</code> qui exécute cet appel HTTP cent fois.</p>
<p><strong>Le seul changement que j'ai apporté à la requête Curl</strong> a été d'ajouter les options <code translate="no">-s</code> pour une sortie silencieuse, <code translate="no">-o /dev/null</code> pour éviter d'afficher le corps de la réponse et, le plus important, <code translate="no">-w "%{time_total}s\n"</code> qui permet de formater la sortie de Curl pour retourner le temps total. Vous pouvez trouver la liste complète des « Write out variables » disponibles <a href="https://everything.curl.dev/usingcurl/verbose/writeout.html#available-write-out-variables" rel="noopener noreferrer">ici</a>.</p>
<p>Et voilà 🎉 ! <strong>Vous pouvez avoir une idée rapide des performances uniquement avec les outils que vous utilisez peut-être déjà.</strong></p>
<p>J'espère que cela vous sera utile !</p>
<p>N'hésitez pas à laisser vos commentaires ou questions ci-dessous.</p>]]>
    </content>
  </entry>
</feed>
