Nos últimos anos a internet evoluiu radicalmente, o acesso aumentou e a cada dia que passa as atividades antes feitas “off-line” passaram a ser “online” como compras, trabalho, negócios e entretenimento. Com esta evolução, sempre temos muitos usuários acessando, navegando e efetuando transações na aplicação. Para garantir que a aplicação aguentará uma certa quantidade de usuários e avaliar a experiência que ele terá na aplicação verificando qual o tempo de resposta a cada iteração, muitas empresas estão aderindo ao teste de performance para garantir a qualidade do seu sistema. Mas afinal, do que realmente se trata o teste de performance?
Simplificando, é aquele em que submetemos o sistema a uma avaliação de carga, stress ou desempenho para avaliar se os resultados estão de acordo com o esperado, garantindo assim a qualidade do sistema. Nestas avaliações, simulamos picos de usuários no sistema para investigar até onde ele suporta. O teste é iniciado com uma carga baixa e vai aumentando gradativamente.
O teste de stress é a avaliação em que colocaremos de primeira o máximo que o sistema aguenta. Já o de desempenho serve para medirmos o que a aplicação já suporta. É executado com uma carga constante e mantido por horas. Neste caso, é feita a análise do tempo de resposta do sistema a cada interação do usuário.
Lembrando que quando estamos falando de teste de performance, não temos uma nomenclatura padronizada. Neste artigo iremos considerar a nomenclatura definida no livro Performance Testing Guidance.
É interessante que a primeira avaliação realizada seja a de carga, para que tenhamos uma perspectiva do quanto o sistema aguenta, para depois definirmos a carga que iremos utilizar no teste de stress e desempenho.
Uma outra forma de definirmos a carga que iremos utilizar em cada avaliação é monitorar o sistema em produção durante um tempo. Se soubermos a quantidade de visitas que o sistema recebe diariamente, quais os horários com mais acessos e quais as funcionalidades mais utilizadas, saberemos quais cargas definir para realizar o teste em cada funcionalidade.
A análise final do teste de performance é realizada através dos dados coletados de sua execução, dos logs do banco de dados e dos logs do servidor de aplicações. É importante, neste cenário, estar em contato com a equipe de infraestrutura e a equipe de redes, pois a análise por completo da estrutura do sistema será mais eficiente e o resultado será mais preciso e abrangerá muitas informações importantes no momento da tomada de decisão sobre os resultados.
Neste artigo serão abordados conceitos utilizados em teste de performance, como teste de carga, stress e desempenho. A ferramenta utilizada para executar o teste é o JMeter e no artigo serão apresentados os principais recursos desta ferramenta, como: requisições, temporizador, extrator de expressão regular, asserções, utilização de variáveis, configuração e execução do teste e análise dos resultados. Veremos como e quando utilizar cada um desses recursos.
JMeter
Quando falamos em teste de performance, sempre a primeira ferramenta que nos vem à cabeça é o JMeter, isso porque é um software que está há um tempo no mercado e tem vários projetos de sucesso relatados na comunidade. Foi criada em 2007 pela Apache Software e é a ferramenta mais utilizada para este seguimento. Pelo fato de ser gratuita e ter o código aberto, podemos desenvolver melhorias para atender melhor o projeto que iremos utilizá-la, como plug-ins para gerar outros relatórios além dos que a ferramenta já oferece.
A instalação do JMeter é bem simples. Seu download pode ser efetuado no site da Apache (ver seção Links). Basta fazer o download e ele estará pronto para uso. Para isso, basta apenas ter o Java 6 ou superior e executar o arquivo .bat que está no pacote baixado do site.
A ferramenta, apesar de ser gratuita, tem muitos recursos para atender ao projeto, como componentes:
- que auxiliam na gestão dos scripts de teste e no controle de cenários aplicados, facilitando a manutenção dos mesmos;
- para configurações de ambientes de testes para execução em servidores e vários terminais;
- para auxiliar nas avaliações das respostas recebidas para as requisições enviadas aos servidores.
Estes são os principais recursos da ferramenta e serão abordados de forma mais detalhada nos tópicos a seguir. Outros recursos que serão apresentados são:
- os controles de gravação que são importantes no momento da gravação dos scripts, ajudando a economizar tempo no projeto;
- o extrator de expressão regular que auxilia a coleta de dados dos resultados retornados para o JMeter, podendo assim realizar validações durante o teste;
- como utilizar variáveis no script de teste para deixar o teste o mais próximo do cenário real do sistema e do seu dia a dia.
Neste artigo iremos falar do JMeter para teste de performance, porém, também é possível criar e executar testes funcionais e em banco de dados, embora existam outras ferramentas no mercado com melhores recursos para executar estes tipos de avaliação.
Criando o script de teste
Assim como em outros tipos de teste, é importante decidir o que deve ser testado e como proceder essas avaliações. Não é interessante avaliar todas as funcionalidades do sistema uma vez que o esforço seria muito alto, então devemos avaliar quais devem ser priorizadas. No caso do teste de performance, devemos considerar dois pontos: funcionalidades que têm muito acesso simultâneo (por exemplo, em um e-commerce, o cenário de compra é o mais importante e o que devemos garantir que tenha performance excelente para que o cliente não passe a comprar em outro local motivado por um desempenho ruim enquanto realizava sua compra) e funcionalidades em que a requisição possa ser mais lenta, como upload e download de arquivo.
Após definir quais funcionalidades serão testadas, devemos dividi-las em casos de testes, que poderão ser organizados por grupos de usuários. Então teremos o plano de teste para agrupar os casos de testes. Todos os casos de testes poderão ser executados simultaneamente ou separadamente. Cada um deles será configurado de uma forma diferente de acordo com o objetivo da funcionalidade. Por exemplo, digamos que os cenários de avaliação definidos foram “Realizar Login”, “Cadastrar Usuário” e “Realizar e Concluir uma Compra”. Nestes cenários podemos definir que o “Login” terá uma carga de 40 usuários simultâneos, o “Cadastrar Usuário” 10 usuários simultâneos e o “Realizar e Concluir uma Compra”, 50 usuários simultâneos. Desta forma, cada caso de teste pode ser adequado ao cenário real que temos em produção. Veremos mais sobre configurações de execução no tópico “Configurações e Execução do Teste”. Depois de definidos todos os casos de testes, iremos criar um script de teste para automatizar sua execução.
O JMeter possui uma estrutura muito fácil para organizar os testes. Na Figura 1 podemos ver a árvore onde ficam todos os componentes utilizados por eles. O componente Plano de Teste que vemos é o responsável por controlar a execução de todos os grupos de usuários. Nele que iremos agrupar todos os componentes. Além disso, também iremos definir algumas configurações das execuções, como se os grupos de usuários devem ser executados simultaneamente ou consecutivamente; ativar o modo de teste funcional para o JMeter salvar dados das respostas do sistema, e; podemos definir variáveis globais que serão utilizadas na execução do teste. Falaremos mais sobre a utilização de variáveis no tópico “Utilizando Variáveis”.
No plano de teste, podemos inserir inúmeros grupos de usuários. Cada um executará um teste diferente podendo ser este executado simultaneamente ou não.
O grupo de usuários é o componente que controla a execução dos cenários. Nele definimos quantos usuários devem interagir com o sistema simultaneamente, qual o tempo de duração e qual ação o JMeter deve tomar quando ocorrer um erro com um usuário.
Para criar o teste devemos adicionar, no grupo de usuários, as requisições necessárias para simular o teste. Relembrando que devemos adicionar um grupo de usuário para cada caso de teste do plano.
Além das requisições, é importante adicionar Asserções para garantir que o teste execute de forma correta. Por exemplo, após realizar um login na aplicação, é interessante inserir uma asserção para garantir que o login foi feito com sucesso antes de realizar o próximo passo. Neste cenário, se a aplicação travar no login, será fácil identificar no relatório final onde está o gargalo.
Devemos também adicionar o temporizador para que o tempo de cada requisição simule o tempo real que o usuário leva para realizar cada iteração com o sistema e o extractor de expressão regular caso a aplicação necessite passar dados de uma resposta para a próxima requisição enviada ao servidor. Por fim, devemos adicionar os componentes ouvintes para realizar a análise após a execução do teste. Nos próximos tópicos, falaremos destes componentes e qual sua importância no teste.
Controlador de gravação
No grupo de usuários, devemos adicionar as requisições que representam cada passo do caso de teste. As requisições podem ser adicionadas uma a uma manualmente ou podemos utilizar um componente que o JMeter oferece chamado controlador de gravação. Este componente faz parte do grupo de controladores lógicos. Ele nos permite gravar a execução de um cenário, com isso todas as requisições são geradas dessa forma sem a necessidade de inserção manual, sendo basicamente um record/play.
Porém, é necessário realizar ajustes nas requisições para a execução ocorrer com sucesso, como trocar os valores fixos por variáveis e ajustar algumas configurações. Por exemplo, em um cenário de download e upload, as configurações da requisição devem ser diferentes de como o controlador de gravação se comporta em cenários comuns de interação com o usuário.
Requisições
Para enviarmos requisições aos servidores, devemos utilizar os componentes do sampler. Nele temos disponíveis várias opções de requisições, como FTP, SOAP/XML-RCP, Java, HTTP, entre outros.
Na Figura 2 temos um exemplo de uma requisição HTTP para realizar um login. O endereço acessado é o 127.0.0.1, já o caminho foi definido para “/Login”. Estamos enviando os parâmetros para realizar o login e o método definido é POST.
Temos várias opções de métodos para definir na requisição, porém os mais utilizados são GET e POST. O GET envia uma requisição ao servidor e ela é somente de leitura ou consulta. Já o POST também envia uma requisição ao servidor, porém ela pode enviar dados para inclusão ou edição de registros.
Nos parâmetros enviados, podemos ver a variável ${USUARIO} declarada. Neste caso, cada usuário enviará a requisição com um dado diferente, porém simultaneamente. Falaremos mais sobre configuração de variáveis no tópico “Utilizando Variáveis”.
Temporizador
Para simular a ação real do usuário na aplicação, devemos emular o Thiking time, ou seja, o tempo que o usuário pensa entre as ações que ele realiza no sistema. O JMeter disponibiliza alguns componentes, como o “temporizador constante” e o “temporizador aleatório uniforme” para simularmos esta ação.
Extrator de expressão regular
Sempre que optamos por realizar teste de performance em um sistema, devemos conhecer a sua estrutura para sabermos que recursos teremos que utilizar para que a avaliação seja executada corretamente. Por exemplo, se a aplicação foi desenvolvida em ASP.NET, teremos que extrair e repassar a VIEWSTATE em todas requisições.
VIEWSTATE é uma solução ASP.NET que armazena as informações e o estado dos controles e objetos da página para se manterem quando um Postback ocorrer na página.
Quando gravamos o teste pela primeira vez com o JMeter, ele armazena a VIEWSTATE atual da gravação. Sendo assim, quando executamos o teste novamente, ele enviará na requisição uma VIEWSTATE inválida. Para solucionarmos isso, deveremos extrair a VIEWSTATE das respostas que recebemos utilizando o componente extrator de expressão regular e repassá-la na próxima requisição.
Na Figura 3 podemos ver uma configuração de expressão regular. Nele está definido que a extração deve ser feita no corpo da resposta (Campo da Resposta a ser verificado), mas poderíamos extrair a expressão de outras partes da resposta, como da URL ou cabeçalho.
No campo Nome de Referência devemos definir o nome da variável onde o JMeter salvará a que será extraída. No caso da Figura 1, a variável foi nomeada para VIEWSTATE. Depois devemos utilizar esta variável como “${VIEWSTATE}” onde for necessário usar a expressão extraída.
No campo Expressão Regular devemos inserir a fórmula para extrair a expressão que queremos. No caso da Figura 3, a expressão é “id="__VIEWSTATE" value="(.+?)"”. Neste caso, o JMeter irá extrair da resposta da requisição o valor onde está o código “(.+?)” e armazená-lo na variável “${VIEWSTATE}”.
Existem várias formas de extrair um dado. É interessante estudar o componente Extração de Expressão Regular para utilizá-lo de forma eficiente em cenários onde a extração é complexa, por exemplo, em casos em que o resultado contenha mais de uma resposta para a expressão simples.
No campo Modelo devemos apontar a quantidade de variáveis que estamos extraindo neste componente. No exemplo da Figura 1, estamos indicando que apenas uma variável será extraída. Se fossem duas variáveis, deveríamos inserir “$1$$2$” no campo. E no momento de utilizar os dados, declararíamos “${VIEWSTATE_g1}” para acessar o dado da primeira variável e “${VIEWSTATE_2}” para acessar o dado da segunda variável.
Por fim, no campo Valor Padrão, definimos o valor que o JMeter utilizará quando não encontrar a expressão regular na resposta da requisição.
Uma forma de avaliar se a expressão regular está correta é conferir a resposta da requisição utilizando o componente ouvinte. Falaremos mais sobre os componentes ouvintes no tópico “Avaliação e relatório da Execução do Teste”.
Asserções
Para garantir que o script de teste esteja executando com sucesso, que o sistema não deu timeout durante a execução, devemos utilizar asserções para verificar o resultado da resposta da requisição que enviamos.
Para validar conteúdo das respostas das requisições, podemos utilizar Extração de Expressão Regular e XPath. Outra asserção que podemos usar é a asserção de duração que é onde definimos o tempo máximo que a aplicação tem para responder a requisição enviada.
Para visualizar o resultado das asserções, devemos utilizar o componente ouvinte. Com o resultado das asserções, podemos inserir condições para o JMeter realizar, por exemplo, quando uma asserção falhar para a execução do teste com aquele usuário.
Utilizando variáveis
Para o teste de performance simular uma situação real, é importante que cada usuário utilize variáveis diferente dos outros usuários simultâneos. No cenário de login, por exemplo, podemos conectar ao sistema utilizando usuários com perfis de acesso diferentes.
Caso a variável utilizada seja a mesma para todos os usuários, é uma boa prática declará-la como variável global no componente plano de teste. Assim, quando quisermos alterá-la, será necessário realizar a alteração em apenas um local.
Para configurar as variáveis por usuário, devemos utilizar o componente Configuração dos dados CSV. Na Figura 4 podemos ver um exemplo de configuração de variáveis com CSV.
Na configuração apresentada, podemos ver que no “Nome do arquivo” é inserido o caminho onde o arquivo CSV está salvo. As variáveis são definidas no campo “Nomes das variáveis”. Estas devem estar na mesma sequência do CSV. Também podemos definir quais usuários utilizarão o CSV na execução. No exemplo temos “Todos os usuários virtuais”. Para declarar as variáveis na requisição devemos utilizar “${}”.
Configurações e execução do teste
Após termos todos os scripts de teste criados, é necessário realizar algumas configurações. Em cada grupo de usuário devemos definir quantos deles executarão aquele teste, tempo de inicialização e tempo de execução. Mas antes, devemos avaliar qual o objetivo do teste para definir como deve ser a configuração realizada. Os objetivos podem ser:
- Avaliar como o sistema se comporta com a carga atual;
- Avaliar qual a carga máxima que o sistema suporta;
- Descobrir os pontos de gargalo;
- Tempo máximo esperado nas requisições.
Na Figura 5 podemos observar um teste configurado para executar 100 usuários (número de usuários virtuais). Ele irá aguardar 10 segundos para inicializar o teste (tempo de inicialização) e irá executar o teste durante uma hora (tempo de início e tempo de término).
Uma outra forma de configurar a quantidade de execuções dos usuários é colocando quantidade no contador de iteração e desabilitar o modo infinito. Desta maneira, o JMeter irá executar o teste com 100 usuários a quantidade de vezes definida neste campo.
No teste da Figura 5 também foi definido que quando ocorrer um erro no teste do usuário, o JMeter deve começar a próxima avaliação a partir deste usuário.
Para garantir o sucesso da execução do teste e que os resultados sejam verdadeiros, o ambiente deve estar bem configurado. Deve-se avaliar a carga que o teste irá executar para definir qual será o tamanho do seu ambiente. A execução pode ser feita na nuvem ou pode ser distribuído em IPs físicos ou virtuais.
Na Figura 6 temos uma representação de uma configuração de ambiente onde o JMeter cliente gerencia todo o teste e, ao iniciar a execução, ele distribui todos os usuários nos quatro IPs que são os JMeter Servers, que por sua vez enviam requisições acessando o alvo de teste.
Avaliação e relatório da execução do teste
A análise dos resultados é a parte mais importante do teste, afinal se passarmos resultados falsos para o cliente ou supervisores, o sistema apresentará erros em produção e teremos retrabalho para a equipe do projeto.
Para monitorar os testes, o JMeter disponibiliza vários componentes que são chamados de ouvintes que podemos adicionar ao plano de teste, em cada grupo de usuário ou em cada requisição. Lembrando que quanto mais ouvintes inserirmos, mais memória consumiremos da máquina na hora da execução e isto pode afetar a execução da avaliação.
A seguir serão apresentados três dos ouvintes mais utilizados para monitorar o teste.
Ouvinte árvore de resultados
O ouvinte árvore de resultados, representado na Figura 7, captura muitas informações de cada requisição feita por cada usuário durante o teste. Por exemplo, quais parâmetros foram enviados na requisição e qual o tempo de resposta do servidor, e exibe o conteúdo da resposta da requisição na aba dados da resposta. Nele também conseguimos visualizar quais variáveis aquela requisição enviou como parâmetro. É possível selecionar várias opções de exibição dos dados da resposta, como: texto, HTML, JSON e JavaScript. Podemos também selecionar as opções XPath Tester e RegExp Tester para testar as fórmulas utilizadas nas extrações de expressões regulares e asserções.
Ouvinte resultado de asserção
Este ouvinte não influencia na geração do relatório, mas é importante para sabermos quais asserções passaram e quais falharam.
Ouvinte relatório de sumário
Esse ouvinte é apresentado na Figura 8, que nada mais é do que uma planilha que o JMeter gera com as principais informações da execução do teste, como: quantidade de usuários (amostras) que executaram cada requisição (rótulo), tempo médio em milissegundos (média), tempo mínimo em milissegundos (mín) e tempo máximo em milissegundos (máx), entre outras. Esses tempos são calculados considerando todas as execuções simultâneas.
Além destes ouvintes, o JMeter tem muitos outros que geram gráficos e fornecem muitas informações importantes. Por exemplo, é interessante também monitorarmos o banco de dados e o servidor da aplicação. Assim, é possível avaliar em que momento do teste a aplicação caiu, se teve picos elevados e qual a carga máxima que a aplicação suportará.
A execução do teste deve ser feita enquanto o sistema não tem nenhum outro acesso, apenas a nossa avaliação rodando para evitar interferência nos resultados.
O teste de performance deve ser trabalhado como um projeto de teste, para o qual precisa ser feito um planejamento completo, estimar o tempo de criação dos scripts, o tempo de execução, tempo de análise e da geração do relatório final.
Este tipo de teste tem o momento certo para ser executado: a aplicação deve estar estável, já deve ter passado por teste funcional, estar sem bugs e não deve ter nenhuma melhoria para ser aplicada durante sua execução. Por exemplo, não é interessante criar um script de teste para uma funcionalidade que ainda sofrerá alterações antes da execução, pois o script terá que ser atualizado, assim gerando retrabalho. O ideal é realizar este teste na versão que será liberada para ou já está em produção, tomando o cuidado com o horário da execução e configurações para não afetar os dados reais de produção.
Neste artigo, a ferramenta utilizada para demonstração foi o JMeter, mas antes de iniciar o projeto de teste de performance em sua organização, é interessante avaliar todas ferramentas no mercado e avaliar qual delas melhor atende aos requisitos de seu projeto.
Referências
- Bringmann, E. and Kramer, A.: Model-based testing of automotive systems. In Software Testing, Verification, and Validation, 2008 1st International Conference on, pages 485-493 (2008).
- Maldonado, J. C.; Auri, E. F. B.; Vincenzi, M. R.; Delamaro, M. E.; Souza, S.; Jino, M. “Introdução ao teste de software”. Instituto de Ciências Matemáticas e de Computação - ICMC/USP, Nota Didática, n. 65, 2004.
- Leitner, A., Ciupa, I., Meyer, B., and Howard, M.: Reconciling manual and automated testing: The autotest experience. In System Sciences, 2007. HICSS 2007. 40th Annual Hawaii International Conference on, pages 261 (2007).
- Moon, H., Kim, G., Kim, Y., Shin, S., Kim, K., and Im, S.: Automation test method for automotive embedded software based on autosar. In Software Engineering Advances, 2009. ICSEA '09. Fourth International Conference on, pages 158-162 (2009).
Links