6.8. Tests de performance automatisés avec JMeter

La performance applicative est un autre domaine de test important. Les tests de performance peuvent être utilisés pour vérifier beaucoup de choses, telles que la rapidité avec laquelle une application répond aux requêtes d'un nombre donné d'utilisateurs simultanés, ou comment une application réagit à un nombre croissant d'utilisateurs. De nombreuses applications ont des contrats de niveau de service (SLAs), qui définissent contractuellement comment elles doivent réagir.

Les tests de performance sont souvent une activité unique, prise en compte uniquement juste à la fin du projet ou quand les choses commencent à aller mal. Néanmoins, les problèmes de performance sont comme n'importe quelle autre sorte de bug : plus tard ils sont détectés dans le processus, plus coûteux ils sont à corriger. Il est donc logique d'automatiser ces tests de performance et de charge, de façon à pouvoir repérer les zones de dégradation des performances avant qu'elles ne sortent dans la nature.

JMeter est un outil open source de test de performance et de charge. Il fonctionne en simulant la charge sur votre application, et en mesurant le temps de réponse alors que le nombre d'utilisateurs simulés et les requêtes augmentent. Il simule les actions d'un navigateur ou une application cliente, envoyant des requêtes de toutes sortes (HTTP, SOAP, JDBC, JMS, etc) vers votre serveur. Vous configurez un ensemble de requêtes à envoyer à votre application, ainsi que des pauses aléatoires, conditions et boucles, et d'autres variantes destinées à mieux imiter les actions utilisateurs réelles.

JMeter s'exécute comme une application Swing, dans laquelle vous pouvez configurer vos scripts de test (voir Figure 6.24, “Préparer un script de test de performance dans JMeter”). Vous pouvez même exécuter JMeter comme proxy, et utiliser votre application dans un navigateur traditionnel pour préparer une version initiale de votre script de test.

Un tutoriel complet sur l'utilisation de JMeter sort du cadre de ce livre. Cependant, il est assez facile à apprendre, et vous pouvez trouver de nombreux détails sur son utilisation sur le site de JMeter. Avec un peu de travail, vous pouvez avoir un script de test respectable et l'exécuter en quelques heures.

Ce qui nous intéresse ici est le processus d'automatisation de ces tests de performance. Il y a plusieurs façons pour intégrer des tests JMeter dans votre processus de build Jenkins. Bien qu'à l'écriture de ces lignes, il n'y ait pas de plugin officiel JMeter pour Maven disponible dans les dépôts Maven, il y a un plugin Ant. Donc, la méthode la plus simple est d'écrire un script Ant pour exécuter vos tests de performance, et ensuite soit d'appeler ce script Ant directement, soit (si vous utilisez un projet Maven, et voulez exécuter JMeter via Maven) d'utiliser l'intégration Ant Maven pour lancer le script Ant à partir de Maven. Un simple script Ant exécutant quelques tests JMeter est illustré ici :

<project default="jmeter">
    <path id="jmeter.lib.path">
      <pathelement location="/jenkins-guide-complet/hudsonbook-content-fr/tools/jmeter/extras/ant-jmeter-1.0.9.jar"/>
    </path>
    
    <taskdef name="jmeter"
             classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask"
             classpathref="jmeter.lib.path" />
    

    <target name="jmeter">
      <jmeter jmeterhome="/jenkins-guide-complet/hudsonbook-content-fr/tools/jmeter"
              testplan="/jenkins-guide-complet/hudsonbook-content-fr/src/test/jmeter/gameoflife.jmx"
              resultlog="/jenkins-guide-complet/hudsonbook-content-fr/target/jmeter-results.jtl">
        <jvmarg value="-Xmx512m" />
      </jmeter>
    </target>
</project>

Cela suppose que l'installation de JMeter est disponible dans le répertoire tools de votre projet. Placer des outils tels que JMeter au sein de votre structure de projet est une bonne habitude, car il rend vos scripts de build plus portables et plus faciles à exécuter sur n'importe quelle machine, ce qui est précisément ce dont nous avons besoin pour les exécuter sur Jenkins.

Préparer un script de test de performance dans JMeter

Figure 6.24. Préparer un script de test de performance dans JMeter


Notons que nous utilisons aussi le tag <jvmarg> optionnel pour fournir à JMeter une quantité de mémoire suffisante — les tests de performance sont une activité consommatrice de mémoire.

Le script affiché ici exécutera les tests de performance JMeter sur une application lancée. Donc vous devez vous assurer que l'application que vous voulez tester est active et lancée avant que vous lanciez vos tests. Il y a plusieurs façons de le faire. Pour des tests de performance plus lourds, vous voudrez généralement déployer votre application sur un serveur de test avant de lancer les tests. Pour la plupart des applications ce n'est généralement pas trop difficile — le plugin Maven Cargo, par exemple, vous permet d'automatiser le processus de déploiement sur une variété de serveurs locaux et distants. Nous verrons aussi comment le faire dans Jenkins plus loin dans le livre.

Sinon, si vous utilisez Maven pour une application Web, vous pouvez utiliser le plugin Jetty ou Cargo pour vous assurer que l'application est déployée avant le démarrage des tests d'intégration, et ensuite appeler le script Ant JMeter à partir de Maven pendant la phase de test d'intégration. Avec Jetty, par exemple, vous pouvez faire quelque chose comme cela :

<project...>
  <build>
    <plugins>
      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>7.1.0.v20100505</version>
        <configuration>
          <scanIntervalSeconds>10</scanIntervalSeconds>
          <connectors>
            <connector
              implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
              <port>${jetty.port}</port>
              <maxIdleTime>60000</maxIdleTime>
            </connector>
          </connectors>
          <stopKey>foo</stopKey>
          <stopPort>9999</stopPort>
        </configuration>
        <executions>
          <execution>
            <id>start-jetty</id>
            <phase>pre-integration-test</phase>
            <goals>
              <goal>run</goal>
            </goals>
            <configuration>
              <scanIntervalSeconds>0</scanIntervalSeconds>
              <daemon>true</daemon>
            </configuration>
          </execution>
          <execution>
            <id>stop-jetty</id>
            <phase>post-integration-test</phase>
            <goals>
              <goal>stop</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      ...
    </plugins>
  </build>
</project>

Cela va démarrer une instance de Jetty et y déployer votre application web avant les tests d'intégration, et l'arrêter après.

Enfin, vous devez exécuter vos tests de performance JMeter pendant cette phase. Vous pouvez le faire en utilisant le maven-antrun-plugin pour lancer le script Ant que nous avons écrit précédemment pendant la phase integration-test :

<project...>
  ...
  <profiles>
    <profile>
      <id>performance</id>
      <build>
        <plugins>
          <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <version>1.4</version>
            <executions>
              <execution>
                <id>run-jmeter</id>
                <phase>integration-test</phase>
                <goals>
                  <goal>run</goal>
                </goals>
                <configuration>
                  <tasks>
                    <ant antfile="build.xml" target="jmeter" >
                  </tasks>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>
  ...
</project>

Maintenant, tout ce que vous devez faire est de lancer les tests d'intégration avec le profil performance pour que Maven exécute la suite de test JMeter. Vous pouvez le faire en invoquant les phases de cycle de vie Maven integration-test ou verify:

$ mvn verify -Pperformance

Une fois que vous avez configuré votre script de build pour gérer JMeter, vous pouvez mettre en place un build de tests de performance dans Jenkins. Pour cela, nous allons utiliser le plugin Jenkins Performance Test, qui interprète les logs JMeter et peut générer des statistiques et des jolis graphes en utilisant ces données. Donc, allez sur l'écran du gestionnaire des plugins sur votre serveur Jenkins et installez ce plugin (voir Figure 6.25, “Préparer un script de tests de performance dans JMeter”). Quand vous aurez installé ce plugin, vous devrez redémarrer Jenkins.

Préparer un script de tests de performance dans JMeter

Figure 6.25. Préparer un script de tests de performance dans JMeter


Une fois que le plugin est installé, vous pouvez mettre en place une tâche de build de performance dans Jenkins. Cette tâche de build sera généralement séparée des autres builds. Dans Figure 6.26, “Mise en place du build de performance pour s'exécuter chaque nuit à minuit”, nous avons mis en place un build de performance fonctionnant sur une base quotidienne, ce qui est probablement assez pour des tests de robustesse ou de performance.

Mise en place du build de performance pour s'exécuter chaque nuit à minuit

Figure 6.26. Mise en place du build de performance pour s'exécuter chaque nuit à minuit


Il ne reste alors plus qu'à configurer votre tâche de build pour qu'elle exécute vos tests de performance. Dans Figure 6.27, “Les tests de performance peuvent demander de grandes quantités de mémoire”, nous exécutons le build Maven que nous avons configuré précédemment. Notez que nous utilisons le champ MAVEN_OPTS (accessible en cliquant sur le bouton Avancé) pour fournir suffisamment de mémoire à la tâche de build.

Les tests de performance peuvent demander de grandes quantités de mémoire

Figure 6.27. Les tests de performance peuvent demander de grandes quantités de mémoire


Pour mettre en place des rapports de performance, sélectionnez simplement l'option “Publish Performance test result report” dans la section Actions à la suite du build (voir Figure 6.28, “Configurer le plugin Performance dans votre tâche de build”). Vous devez indiquer à Jenkins où se trouvent les résultats de test JMeter (les fichiers de sortie, pas les scripts de test). Le plugin Performance peut traiter plusieurs résultats JMeter, donc vous pouvez mettre des caractères génériques dans le chemin pour vous assurer que tous vos rapports JMeter seront affichés.

Si vous prenez vos mesures de performance au sérieux, alors le build doit échouer si les niveaux de service attendus ne sont pas atteints. Dans un environnement d'intégration continue, toute mesure qui n'échoue pas si un critère de qualité minimum n'est pas atteint a tendance à être ignorée.

Vous pouvez configurer le plugin Performance afin de marquer un build instable ou échoué si un certain pourcentage des requêtes finissent en erreur. Par défaut, ces valeurs ne seront atteintes que dans les cas d'erreurs applicatives (i.e., bugs) ou plantages de serveur. Toutefois, vous devriez vraiment configurer vos scripts de test JMeter pour placer une limite sur le maximum acceptable pour le temps de réponse pour vos requêtes. Ceci est particulièrement important si votre application a des obligations contractuelles à cet égard. Une façon de le faire avec JMeter est d'ajouter un élément Duration Assertion à votre script. Cela va provoquer une erreur si une requête prend plus d'un certain temps pour s'exécuter.

Configurer le plugin Performance dans votre tâche de build

Figure 6.28. Configurer le plugin Performance dans votre tâche de build


A présent, lorsque la tâche de build s'exécute, le plugin Performance va produire des graphes permettant de suivre les temps de réponse globaux et le nombre d'erreurs (voir Figure 6.29, “Le plugin Jenkins Performance garde une trace des temps de réponse et des erreurs”). Il y aura un graphe séparé pour chaque rapport JMeter que vous avez généré. S'il n'y a qu'un seul graphe, il sera aussi affiché sur la page d'accueil du build ; sinon vous pouvez les visualiser sur une page dédiée à laquelle vous pouvez accéder via le menu Performance Trend.

Le plugin Jenkins Performance garde une trace des temps de réponse et des erreurs

Figure 6.29. Le plugin Jenkins Performance garde une trace des temps de réponse et des erreurs


Ce graphe vous donne un aperçu de la performance dans le temps. Vous utilisez généralement ce graphe pour vous assurer que les temps de réponse moyens sont dans la limite prévue, et aussi repérer des variations anormales des temps de réponse moyens ou maximums. Cependant, si vous avez besoin de suivre et d'isoler des problèmes de performance, l'écran Performance Breakdown peut être plus utile. A partir du rapport Performance Trend, cliquez sur le lien Last Report en haut de l'écran. Cela fera apparaître une répartition des temps de réponse et des erreurs par demande (voir Figure 6.30, “Vous pouvez aussi visualiser les résultats de performance par requête”). Vous pouvez faire la même chose pour les builds précédents, en cliquant sur le lien Performance Report sur la page de détails du build.

Avec quelques variations mineures, un script de test JMeter travaille essentiellement en simulant un nombre donné d'utilisateurs simultanés. Généralement, cependant, vous voudrez voir comment votre application gère des nombres différents d'utilisateurs. Le plugin Jenkins Performance le prend en compte assez bien, et peut traiter des graphes de plusieurs rapports JMeter. Assurez-vous juste que vous utilisez une expression générique quand vous dites à Jenkins où se trouvent les rapports.

Bien sûr, il serait agréable de pouvoir réutiliser le même script de test JMeter pour chaque test exécuté. JMeter supporte les paramètres, donc vous pouvez facilement réutiliser le même script JMeter avec un nombre différent d'utilisateurs simulés. Utilisez simplement une expression de propriété dans votre script JMeter, et passez cette propriété à JMeter quand vous lancez le script. Si votre propriété est nommée request.threads, alors l'expression de propriété dans votre script JMeter sera ${__property(request.threads)}. Ensuite, vous pouvez utiliser l'élément <property> dans la tache Ant <jmeter> pour passer la propriété quand vous exécutez le script. La cible Ant suivante, par exemple, exécute JMeter trois fois, pour 200, 500 et 1000 utilisateurs simultanés:

    <target name="jmeter">
      <jmeter jmeterhome="/jenkins-guide-complet/hudsonbook-content-fr/tools/jmeter"
              testplan="/jenkins-guide-complet/hudsonbook-content-fr/src/test/jmeter/gameoflife.jmx"
              resultlog="/jenkins-guide-complet/hudsonbook-content-fr/target/jmeter-results-200-users.jtl">
        <jvmarg value="-Xmx512m" />
        <property name="request.threads" value="200"/>
        <property name="request.loop" value="20"/>
      </jmeter>
      <jmeter jmeterhome="/jenkins-guide-complet/hudsonbook-content-fr/tools/jmeter"
              testplan="/jenkins-guide-complet/hudsonbook-content-fr/src/test/jmeter/gameoflife.jmx"
              resultlog="/jenkins-guide-complet/hudsonbook-content-fr/target/jmeter-results-500-users.jtl">
        <jvmarg value="-Xmx512m" />
        <property name="request.threads" value="500"/>
        <property name="request.loop" value="20"/>
      </jmeter>
      <jmeter jmeterhome="/jenkins-guide-complet/hudsonbook-content-fr/tools/jmeter"
              testplan="/jenkins-guide-complet/hudsonbook-content-fr/src/test/jmeter/gameoflife.jmx"
              resultlog="/jenkins-guide-complet/hudsonbook-content-fr/target/jmeter-results-1000-users.jtl">
        <jvmarg value="-Xmx512m" />
        <property name="request.threads" value="1000"/>
        <property name="request.loop" value="20"/>
      </jmeter>
    </target>
Vous pouvez aussi visualiser les résultats de performance par requête

Figure 6.30. Vous pouvez aussi visualiser les résultats de performance par requête