Planifier des tests de charge en utilisant Apache JMeter

Effectué

Dans cette section, vous allez explorer les tests de charge et découvrir comment les ajouter au pipeline. Les tests de charge utilisent Apache JMeter pour simuler plusieurs utilisateurs accédant simultanément à l’application web. Les tests extraient le contenu web de l’application qui s’exécute sur Azure App Service dans l’environnement Préproduction.

Tim commence en affichant l’interface utilisateur Apache JMeter sur un ordinateur portable. Il exécute un plan de test de base. Tim et Mara exportent ensuite le plan de test dans un fichier qui peut être exécuté à partir de la ligne de commande. Enfin, elles ajoutent des tâches à Azure Pipelines pour exécuter les tests de charge lors de la phase Préproduction.

Notes

Par gagner du temps, vous pouvez ne pas installer Apache JMeter sur votre ordinateur local. Vous pouvez simplement lire ce qui suit.

Exécuter des tests de charge à partir d’Apache JMeter

Apache JMeter est un outil de test de charge open source qui analyse et mesure le niveau de performance. Le rapport qu’il génère est un fichier XML.

Azure Pipelines peut lire le rapport Apache JMeter et générer un graphique. Vous n’avez pas besoin de matériel particulier pour exécuter ces tests : il suffit pour cela d’utiliser un agent hébergé par Microsoft. Dans le scénario Space Game, il est probable que vous exécuteriez ces tests dans un environnement intermédiaire.

Créer le plan de test

Voici comment se présente Apache JMeter sur un ordinateur portable exécutant Linux :

Screenshot of the Apache JMeter user interface.

Pour commencer, vous créez un fichier de plan de test, par exemple LoadTest.jmx. Vous ajoutez ensuite un groupe de threads (Thread Group) au fichier. Chaque utilisateur simulé s’exécute sur son propre thread. Un groupe de threads contrôle le nombre d’utilisateurs et le nombre de requêtes de chaque utilisateur.

L’exemple suivant montre 10 utilisateurs (threads) simulés. Sachant que chaque utilisateur effectue 10 requêtes, le système obtient au total 100 requêtes.

Screenshot of specifying the thread group in Apache JMeter.

Un échantillonneur est une requête effectuée par JMeter. JMeter peut interroger des serveurs via HTTP, FTP, TCP et plusieurs autres protocoles. Les échantillonneurs génèrent les résultats qui sont ajoutés au rapport.

Ensuite, vous ajoutez les valeurs par défaut des requêtes HTTP (HTTP Request Defaults) et un échantillonneur de requête HTTP (HTTP Request) au groupe de threads. Vous indiquez le nom d’hôte du site web Space Game qui s’exécute dans l’environnement intermédiaire sur Azure App Service.

Screenshot that shows specifying the HTTP request in Apache JMeter.

Le scénario précédent crée un plan de test de base.

Exécuter le plan de test

JMeter vous permet d’exécuter divers types de tests. Il est possible d’exécuter votre plan de test à partir de l’interface utilisateur graphique de JMeter. Cependant, pour les tests de charge, la documentation JMeter vous recommande d’exécuter le plan de test depuis la ligne de commande.

Pour ce faire, vous devez utiliser la commande suivante :

apache-jmeter-5.4.1/bin/./jmeter -n -t LoadTest.jmx -o Results.xml

L’argument -n spécifie d’exécuter JMeter en mode sans interface graphique utilisateur. L’argument -t spécifie le fichier du plan de test, LoadTest.jmx. L’argument -o spécifie le fichier du plan de rapport, Results.xml.

JMeter s’exécute et produit le fichier de rapport, Results.xml. Cet exemple de fichier montre les premiers résultats :

<?xml version="1.0" encoding="UTF-8"?>
<testResults version="1.2">
<httpSample t="180" it="0" lt="95" ct="35" ts="1569306009772" s="true" lb="HTTP Request" rc="200" rm="OK" tn="Thread Group 1-1" dt="text" by="40871" sby="144" ng="1" na="1">
  <java.net.URL>http://tailspin-space-game-web-staging-1234.azurewebsites.net/</java.net.URL>
</httpSample>
<httpSample t="174" it="0" lt="96" ct="38" ts="1569306009955" s="true" lb="HTTP Request" rc="200" rm="OK" tn="Thread Group 1-1" dt="text" by="40869" sby="144" ng="1" na="1">
  <java.net.URL>http://tailspin-space-game-web-staging-1234.azurewebsites.net/</java.net.URL>
</httpSample>
<httpSample t="160" it="0" lt="121" ct="35" ts="1569306010131" s="true" lb="HTTP Request" rc="200" rm="OK" tn="Thread Group 1-1" dt="text" by="40879" sby="144" ng="2" na="2">
  <java.net.URL>http://tailspin-space-game-web-staging-1234.azurewebsites.net/</java.net.URL>
</httpSample>

Chaque échantillon produit un nœud dans le rapport. L’attribut t spécifie le temps de réponse en millisecondes. Comme vous pouvez le constater ici, il y a trois requêtes qui ont pris 180 ms, 174 ms et 160 ms.

Idéalement, la durée moyenne des requêtes doit être inférieure à une seconde. Au maximum 10 % des requêtes peuvent prendre plus d’une seconde. Vous pouvez configurer JMeter pour qu’il calcule des statistiques comme le temps de réponse minimal, maximal et moyen, ou l’écart type. Vous pouvez écrire un script pour lui permettre de fournir ces informations.

Pour visualiser les résultats des tests, vous devez les fournir dans un format que comprend Azure Pipelines. Azure Pipelines peut analyser un fichier XML qui contient les résultats des tests, mais le fichier doit être dans un format pris en charge. Les formats pris en charge sont CTest, JUnit (y compris PHPUnit), NUnit 2, NUnit 3, Visual Studio Test (TRX) et xUnit 2. Vous pouvez écrire un fichier XSLT qui convertit les résultats de JMeter au format JUnit.

Transformer le rapport en JUnit

XSLT signifie XSL Transformations ou eXtensible Stylesheet Language Transformations. Un fichier XSLT ressemble à un fichier XML, mais il vous permet de transformer un document XML en un autre format XML.

Pour rappel, voici nos exigences en matière de tests de charge :

  • La durée moyenne d’une requête doit être inférieure à une seconde.
  • Au maximum 10 % des requêtes peuvent prendre plus d’une seconde.

Voici à quoi ressemble un fichier XSLT qui répond à ces exigences :

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://exslt.org/math">
  <xsl:output method="xml" indent="yes" encoding="UTF-8"/>
  <xsl:template match="/testResults">
    <xsl:variable name="times" select="./httpSample/@t" />
    <xsl:variable name="failures" select="./httpSample/assertionResult/failureMessage" />
    <xsl:variable name="threshold" select="1000" />
    <testsuite>
      <xsl:attribute name="tests"><xsl:value-of select="count($times)" /></xsl:attribute>
      <xsl:attribute name="failures"><xsl:value-of select="count($failures)" /></xsl:attribute> 
      <testcase>
          <xsl:variable name="avg-time" select="sum($times) div count($times)" />
          <xsl:attribute name="name">Average Response Time</xsl:attribute>
          <xsl:attribute name="time"><xsl:value-of select="format-number($avg-time div 1000,'#.##')"/></xsl:attribute>
          <xsl:if test="$avg-time > $threshold">
            <failure>Average response time of <xsl:value-of select="format-number($avg-time,'#.##')"/> exceeds <xsl:value-of select="$threshold"/> ms threshold.</failure>
          </xsl:if> 
      </testcase>
      <testcase>
          <xsl:variable name="exceeds-threshold" select="count($times[. > $threshold])" />
          <xsl:attribute name="name">Max Response Time</xsl:attribute>
          <xsl:attribute name="time"><xsl:value-of select="math:max($times) div 1000"/></xsl:attribute>
          <xsl:if test="$exceeds-threshold > count($times) * 0.1">
            <failure><xsl:value-of select="format-number($exceeds-threshold div count($times) * 100,'#.##')"/>% of requests exceed <xsl:value-of select="$threshold"/> ms threshold.</failure>
          </xsl:if>
      </testcase>
    </testsuite>
  </xsl:template>
</xsl:stylesheet>

Nous n’allons pas approfondir ici le fonctionnement de XSL. Pour résumer, ce fichier commence par collecter les données suivantes à partir de la sortie de JMeter :

  • La durée de chaque requête HTTP.

    Il collecte ces données en sélectionnant l’attribut t dans chaque élément httpSample. (./httpSample/@t)

  • Chaque message d’échec.

    Il collecte ces données en sélectionnant tous les nœuds failureMessage du document. (./httpSample/assertionResult/failureMessage)

Le fichier XSLT définit également la valeur de seuil sur 1 000 ms. Ce temps de réponse est la valeur maximale que nous avons définie précédemment.

À partir de ces variables, le fichier XSLT fournit le nombre total de tests et le nombre total d’échecs. Il fournit ensuite ces deux cas de test :

  • Le temps de réponse moyen et un échec si la moyenne dépasse le seuil de 1 000 ms.
  • Le temps de réponse maximal et un échec si plus de 10 % des requêtes dépassent le seuil de 1 000 ms.

Les résultats de la transformation XSLT correspondent au format JUnit, qui est lisible par Azure Pipelines. Vous pouvez nommer le fichier XSLT JMeter2JUnit.xsl.

Ensuite, vous avez besoin d’un processeur XSLT. Dans cet exemple, nous allons utiliser xsltproc, qui est un outil en ligne de commande permettant d’appliquer des feuilles de style XSLT à des documents XML.

Vous pouvez installer xsltproc comme suit :

sudo apt-get install xsltproc

Exécutez ensuite xsltproc pour transformer le rapport JMeter en rapport JUnit :

xsltproc JMeter2JUnit.xsl Results.xml > JUnit.xml

Voici le fichier JUnit résultant, JUnit.xml :

<?xml version="1.0" encoding="UTF-8"?>
<testsuite xmlns:math="http://exslt.org/math" tests="100" failures="0">
  <testcase name="Average Response Time" time="0.17"/>
  <testcase name="Max Response Time" time="0.373"/>
</testsuite>

Dans cet exemple, le temps de réponse moyen est de 170 ms. Le temps de réponse maximal est de 373 ms. Ni l’un ni l’autre des cas de test ne génère de défaillance, car les deux temps de réponse sont inférieurs au seuil de 1 000 ms.

Vous allez bientôt exécuter ces tests dans le pipeline. Vous pouvez considérer Results.xml, le fichier écrit par JMeter, comme un fichier intermédiaire qui n’est pas publié sur les résultats des tests du pipeline. JUnit.xml est le fichier de rapport final. Ce fichier est publié sur le pipeline pour que l’équipe puisse visualiser les résultats.