Planejar testes de carga usando o Apache JMeter

Concluído

Nesta seção, você explorará o teste de carga e aprenderá a adicionar testes de carga ao pipeline. Os testes de carga usam o Apache JMeter para simular vários usuários que acessam o aplicativo Web simultaneamente. Os testes buscam o conteúdo da Web do aplicativo que é executado no Serviço de Aplicativo do Azure no ambiente de Preparo.

Tim começa abrindo a interface do usuário do Apache JMeter em um laptop. Ele executa um plano de teste básico. Em seguida, Tim e Clara exportam o plano de teste para um arquivo que pode ser executado na linha de comando. Por fim, eles adicionam tarefas ao Azure Pipelines para executar os testes de carga durante o Preparo.

Observação

Para fins de brevidade, você não precisa instalar o Apache JMeter no computador local. Você pode apenas acompanhar a leitura.

Executar testes de carga do Apache JMeter

O Apache JMeter é uma ferramenta de teste de carga de software livre que analisa e mede o desempenho. O relatório gerado é um arquivo XML.

O Azure Pipelines pode ler o relatório do Apache JMeter e gerar um grafo. Você não precisa de nenhum hardware especial para executar esses testes, portanto, você pode usar um agente hospedado pela Microsoft para executá-los. No cenário do Space Game, você executaria esses testes provavelmente na pré-produção.

Criar o plano de teste

Veja a aparência do Apache JMeter em um laptop que executa o Linux:

Captura de tela da interface do usuário do Apache JMeter.

Você criaria um novo arquivo de plano de teste; por exemplo, LoadTest.jmx. Em seguida, você adicionaria um Grupo de Threads ao arquivo. Cada usuário simulado é executado em seu próprio thread. Um grupo de threads controla o número de usuários e o número de solicitações de cada usuário.

O exemplo a seguir mostra 10 usuários simulados (threads). Cada usuário faz 10 solicitações, portanto, o sistema obtém um total de 100 solicitações.

Captura de tela da especificação do grupo de threads no Apache JMeter.

Um sampler é uma única solicitação feita pelo JMeter. O JMeter pode consultar servidores por HTTP, FTP, TCP e vários outros protocolos. Os amostradores geram os resultados que são adicionados ao relatório.

Em seguida, você adicionará Http Request Defaults e um sampler de Http Request ao grupo de threads. Você forneceria o nome do host do site do Space Game que é executado no ambiente de preparo no Serviço de Aplicativo do Azure.

Captura de tela que mostra a especificação da solicitação HTTP no Apache JMeter.

O cenário anterior cria um plano de teste básico.

Executar o plano de teste

O JMeter permite que você execute muitos tipos de testes. É possível executar seu plano de teste na interface gráfica do usuário do JMeter. Para testes de carga, no entanto, a documentação do JMeter recomenda que você execute o plano de teste na linha de comando.

Você executaria o plano de teste usando este comando:

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

O -n argumento especifica para executar o JMeter no modo não GUI. O -t argumento especifica o arquivo de plano de teste, LoadTest.jmx. O -o argumento especifica o arquivo de relatório, Results.xml.

O JMeter é executado e produz o arquivo de relatório, Results.xml. Este exemplo do arquivo mostra os primeiros resultados:

<?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>

Cada exemplo produz um nó no relatório. O t atributo especifica o tempo de resposta em milissegundos (ms). Aqui, você verá três solicitações que levaram 180 ms, 174 ms e 160 ms.

Os tempos de solicitação ideais devem ter uma média inferior a um segundo. Não mais do que 10% das solicitações devem levar mais de um segundo. Você pode configurar o JMeter para relatar estatísticas como os tempos mínimos, máximos e médios de resposta ou o desvio padrão. Você pode escrever um script para ajudar a fornecer essas informações.

Para visualizar os resultados do teste, você precisa fornecê-los em um formato que o Azure Pipelines entenda. O Azure Pipelines pode analisar um arquivo XML que contém resultados de teste, mas o arquivo precisa estar em um formato com suporte. Os formatos com suporte incluem CTest, JUnit (incluindo PHPUnit), NUnit 2, NUnit 3, TRX (Visual Studio Test) e xUnit 2. Você pode escrever um arquivo XSLT que converte os resultados do JMeter em JUnit.

Transformar o relatório em JUnit

XSLT significa Transformações XSL, ou eXtensible Stylesheet Language Transformations. Um arquivo XSLT se assemelha a um arquivo XML, mas permite transformar um documento XML em outro formato XML.

Lembre-se de nossos requisitos para testes de carga:

  • O tempo médio de solicitação deve ser menor que um segundo.
  • Não mais do que 10% das solicitações devem levar mais de um segundo.

Veja como é um arquivo XSLT que atenda a esses requisitos:

<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>

Não vamos nos aprofundar em como o XSL funciona aqui. Mas, para resumir, esse arquivo primeiro coleta os seguintes dados da saída do JMeter:

  • Tempo de cada solicitação HTTP.

    Ele coleta esses dados selecionando o t atributo de cada httpSample elemento. (./httpSample/@t)

  • Cada mensagem de falha.

    Ele coleta esses dados selecionando todos os failureMessage nós do documento. (./httpSample/assertionResult/failureMessage)

O arquivo XSLT também define o valor limite como 1.000 ms. Esse tempo de resposta é o máximo que definimos anteriormente.

Considerando essas variáveis, o arquivo XSLT fornece o número total de testes e o número total de falhas. Em seguida, ele fornece estes dois casos de teste:

  • O tempo médio de resposta e uma falha se a média exceder o limite de 1.000 ms.
  • O tempo máximo de resposta e uma falha se mais de 10% das solicitações excederem o limite de 1.000 ms.

Os resultados do XSLT correspondem ao formato JUnit, que o Azure Pipelines entende. Você pode nomear o arquivo XSLT JMeter2JUnit.xsl.

Em seguida, você precisaria de um processador XSLT. Neste exemplo, usaremos o xsltproc, que é uma ferramenta de linha de comando para aplicar folhas de estilo XSLT a documentos XML.

Você pode instalar o xsltproc da seguinte maneira:

sudo apt-get install xsltproc

Em seguida, você executaria xsltproc para transformar o relatório JMeter em JUnit:

xsltproc JMeter2JUnit.xsl Results.xml > JUnit.xml

Este é o arquivo JUnit resultante, 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>

Neste exemplo, o tempo médio de resposta é de 170 ms. O tempo máximo de resposta é 373 ms. Nenhum caso de teste gera uma falha, pois ambas as vezes ficam abaixo do limite de 1.000 ms.

Em breve, você executará esses testes na linha de montagem. Você pode considerar Results.xml, o arquivo que o JMeter grava, como um arquivo intermediário que não é publicado para os resultados de teste do pipeline. JUnit.xml é o arquivo de relatório final. Esse arquivo é publicado no pipeline para que a equipe possa visualizar os resultados.