fbpx

Testes End-To-End usando Cypress e Cucumber

Desenvolvimento

Introdução

Atualmente a eficiência é algo essencial para se manter competitivo em qualquer setor. Em tecnologia não seria diferente. Empregar da melhor forma possível os recursos de sua empresa deve ser sempre uma prioridade, pois gera menores custos e reduz os riscos de forma geral. No tocante a recursos humanos esse discurso é ainda mais importante, pois se tratando de uma área que exige grande qualificação também implica em um alto custo para o empreendedor.

Uma forma de otimizar o uso dos recursos humanos em uma empresa de TI é a utilização da automação de testes. Dessa forma também é possível garantir uma melhor qualidade ao cliente, aumentar a confiança no produto e fornecer suporte ao time de desenvolvimento.

A primeira etapa para a implementação de testes é conhecer exatamente o que se quer testar e a quem esse teste dará suporte. Para essa tarefa existem alguns modelos, dentre eles o Agile Testing Quadrants, desenvolvido por Brian Marick.

Quadro 1 – Agile Testing Quadrants, por Brian Marick

Basicamente esse modelo subdivide os testes em quatro quadrantes. Os quadrantes mais a esquerda guiam o desenvolvimento enquanto os mais a direita criticam o produto. Os mais acima se voltam para questões relacionadas ao negócio e os mais abaixo são relacionados a tecnologia. Assim, existe uma grande variedade de testes e a implementação destes dependem da necessidade individual de cada projeto. É uma matéria multidisciplinar e deve ser conversado com todos os envolvidos para determinar o que realmente é necessário.

Powered by Rock Convert

O objetivo deste artigo é apresentar brevemente conceitos relativos ao framework Cypress associado ao plugin Cucumber para testes end to end. Será utilizado a abordagem Behavior Test Driven, portanto o comportamento do usuário será utilizado para a criação dos testes.

Cypress como framework end to end

Instalação

Graças ao nosso bom e velho Isaac, o Cypress e o plugin Cucumber serão instalado pelos gerenciadores de pacotes npm. Para tanto basta simplesmente digitar a fórmula mágica abaixo, na pasta raiz do seu projeto web.

npm install —save-dev cypress cypress-cucumber-preprocessor 


Configuração

A primeira coisa a se fazer é adicionar os scripts de teste ao seu package.json, se o npm já não tiver feito isso.

Figura 1 – Scripts necessários para que o Cypress funcione supimpa e configuração prévia do cucumber pré-processor.

Configurar o cucumber dessa forma faz com que os testes sejam globais e sejam escritos em em cypress/support/step_definitions. Também podemos definir de forma não global e para isso basta colocar “nonGlobalStepDefinition” como true (comentado acima) e remover “step_definitions”. Mas qual a diferença? Bom, nesse caso se seu teste tivesse o nome cypress/integration/teste.feature a sua implementação seria definida em cypress/integration/teste/*.js. A escolha de um em detrimento ao outro dependerá muito da preferência pessoal de cada um.

Para utilizar o test runner, uma interface gráfica que permite o usuário acompanhar no próprio browser o seu teste, utilize o seguinte comando.

npm run cypress:open

Ou então, se deseja rodar tudo em linha de comando com um relatório minimalista, use o comando que se segue.

npm run cypress:run

A seguir crie um arquivo cypress.json na pasta raiz de seu projeto. Este arquivo contém configurações do Cypress. Abaixo está como o meu projeto foi configurado.

Figura 2 – Arquivo de configuração Cypress.json

Neste arquivo várias coisas serão definidas, que implicarão no fortúnio ou não dos nossos teste:

  • Video: pode ser true ou false. Define se os videos serão gravados em cypress/videos. Particularmente útil quando rodamos em linha de comando.
  • baseUrl: é um item muito importante, essa é a url base do seu projeto. Definindo isso aqui evita que seja necessário digitar o endereço todo durante a implementação do teste e também facilita a execução deste quando você trocar de ambiente.
  • viewportWidth e viewportHeigh é a resolução da tela que o teste será executado.
  • IgnoreTestFiles: definirá os arquivos que serão ignorados em cypress/integration. Como será utilizado o Cucumber nesse exemplo, então os arquivos JS serão ignorados.

Finalmente vamos mudar algumas coisas em cypress/plugins/index.js, temos que dar load no plugin do Cucumber. Para isso, inclua a seguinte fórmula mágica no arquivo em questão.

Figura 3 – Carregando o nosso plugin

Terminamos a parte burocrática e podemos começar a diversão. Se tudo der certo e eu não infringir nenhum copyright, na próxima seção será demonstrado como podemos construir lindos testes end-to-end.

Testes fantásticos e onde habitam

Será utilizado um exemplo de teste de login que desenvolvi aqui na Ez. Basicamente faz um post request para pegar o token de autenticação de admin. Armazena no local storage para termos permissão. Ai ele cadastra uma série de usuários e redefine as senhas para a padrão (123456). Assim, ele tenta logar para cada usuário e caso obtenha sucesso significa que o teste passou.

Primeiramente definiremos dois comandos personalizados em cypress/support/commands.js para salvar nosso localStorage de forma fácil e também para apagá-lo. A vantagem é que dessa forma não precisamos logar/deslogar no sistema pela tela de login. Isso demandaria muito tempo e essa tela, no caso de login para o admin, já foi testada em outro momento. Então seria uma tarefa repetitiva e consumiria tempo desnecessário. Além disso, adicionaremos um comando para que seja menos burocrático fazer requisições. Assim, nosso commands.js ficará da seguinte forma:

Figura 4 – Comando para fazer requisições na API, com autenticação via token
  • cy.authRequest(método, opções): serve para fazer requests no nosso ambiente. Repare que ele já pega o token do localStorage e isso facilita nossa vida, pois caso contrário precisaríamos ficar repetindo esse código sempre que quiséssemos fazer um request. É um comando versátil que permite inclusive mockar dados, pois opções pode ser um objeto contendo {url: link, fix: obj ou string}. Um objeto pode ser mocado diretamente na requisição. Quando vem como string devemos usar o comando cy.fixture(‘fixture:NOME’). Ele vai pegar o json com o NOME contido em cypress/fixture, vai dar um parse nele e ai podemos pegar o objeto retornado e sair usando numa boa.
Figura 5 – Comando para resetar o local storage
  • cy.resetLocalStorage(): simplesmente limpa o local Storage. Único segredo é que envelopei o window.localStorage.clear(); dentro de um cy.wrap(null). Isso tem que ser feito, pois caso contrário o comando fica fora de sincronia em nosso teste.
Figura 6 – Comando para definir um novo localStorage baseado em um usuário
  • cy.setLocalStorage(usuário): Passamos um login de um usuário e esse comando já fará um post request com a senha padrão 123456 e definirá nosso local storage.

Repare que foi utilizado o comando Cypress.env(‘Chave’). Esse comando basicamente pegar o valor da chave que foi definida no arquivo cypress.env.json na pasta raiz. É uma forma prática de configurarmos tudo de forma genérica, para facilitar a migração para outro ambiente. Outro detalhe é que tudo foi escrito em forma de clousure. Veja que o resetLocalStorage está dentro de um cy.wrap(null).then(() => { /*seu código */ }). Isso foi feito porque tudo no cypress é uma promise. Se você não fizer dessa forma seu comando será executado em um momento errado da execução de teste e isso pode fazer com que as coisas saiam do controle. Não utilize async/await, pois fará com que o cypress se comporte de maneira inesperada.

Portanto sempre que quiser definir novos comandos utilize este método. Facilitará sua vida e pode te poupar de repetir código e também será uma mão na roda se você precisar fazer uma manutenção nesse código, já que está tudo em um único lugar.

Vamos começar a escrever nosso teste. Eu gosto sempre de iniciar o *.feature, pois já podemos imaginando tudo que vamos precisar. Experimente abaixo o poder semântico adicionado pelo grandioso Cucumber.

Figura 7 – Poder semântico do Cucumber!

Este pequeno teste mostra o quão prático fica escrever testes utilizando cucumber. Primeiramente o arquivo cypress/integration/*.fixture tem algumas seções:

  • Feature: onde terá o nome da sua feature e uma breve descrição abaixo do que está sendo testado
  • Background: onde você definirá o que será executado antes de cada teste. Nesse caso eu preciso sempre ser admin, pois é o usuário que tem permissão para adicionar outros usuários
  • Scenario: onde a mágica acontece, são os testes propriamente ditos

Abaixo começamos nosso *.js. A primeira linha serve para fazer o IDE e o ESlint felizes, não tem importância no código. Ai declarei três variáveis fora dos testes e utilizarei isso para compartilhar o contexto entre eles e conseguir manter a semântica do teste.

Figura 8 – Compartilhando contexto pelas variáveis.

Abaixo vem os When’s. Note como recupero o login e o nome de usuário no primeiro when. O segundo utiliza uma rota exposta na API que serve para redefinir a senha do usuário para a padrão. Passo o userId, que foi o contexto compartilhado entre estas duas etapas, no body. Expect é usado para fazer uma ASSERTION que nada mais é um estado esperado do app. Nesse caso só estou verificando se o status code da API é 200, mas poderia fazer inúmeras Assertions, o céu é o limite.

Figura 9 – Definindo os ‘When’

Seguimos definindo outras etapas do teste. Basicamente buscamos o primeiro elemento com classe ‘.el-input__inner’ e digitamos o login, que está no contexto compartilhado entre os testes. Também digitamos a senha padrão e tentamos fazer login. O nome do usuário deve estar visível nesse momento e esta será nossa Assertion! Aproveitamos para deletar o usuário recém criado. O teste está feito e ele não é mais necessário. É preciso apagar o localStorage, pois logamos como admin para criar esse usuário. Se não limparmos não será pedido novo login.

Figura 10 – Definindo os ‘Then’

Aqui está nosso plano de fundo, o que será executado antes de todo Scenario. Nesse caso está sendo ‘setado’ o localStorage, de forma totalmente genérica, usando o comando criado em commands.js. O nome de usuário é passado pelo arquivo *.feature em forma de string. Como todos os Given, When e Then são globais, esse Given poderia muito bem ficar em um arquivo support/step_definitions/common.js, pois pode ser reutilizado em diversos testes.

Figura 13 – Given – o nosso background!

As implementações ficam a cargo do cypress/support/step_definition/*.js. No caso foram definidas todas as etapas dos testes, repare como foram utilizados os comandos definidos em commands.js. Também repare que parte das informações são definidas no *.feature. Dessa forma podemos escrever os testes diretamente lá de forma genérica e passar parâmetros para o nosso *.js sem precisar ficar reescrevendo o javascript.

Powered by Rock Convert

Conclusão

O cypress é um framework poderosíssimo para testes end-to-end. É claro que arranhamos apenas sua superfície. Associando ao plugin cucumber temos testes intuitivos com grande significado semântico e que pode colaborar para a escrita de testes genéricos, economizando tempo de desenvolvimento e tornando o uso dos já escassos recursos humanos mais eficiente.

Entre com seus dados para a ligação.