Mudando do Selenium para o Cypress
Logo que ingressei na Aurum como QA do Themis, eu recebi um legado de uma suíte de testes E2E baseada no Selenium e feita em Java. Após um período inicial para entender o padrão Page Object e adquirir o conhecimento do projeto e da linguagem, comecei a corrigir e criar testes nessa suíte.
Conforme trabalhei nessa suíte de testes, notei que estava gastando mais tempo corrigindo testes do que criando novos, além de limitações ao lidar com elementos dinâmicos e processos assíncronos, por exemplo, o select2 do framework angular js e a verificação do carregamento da página evitar de selecionar elementos ainda não renderizados.
No que tange a questão de manutenibilidade da suíte de testes;além de exigir um conhecimento mais técnico, o Selenium necessita de um caminho muito específico para selecionar um elemento, portanto, qualquer pequena alteração de layout quebra muitos testes.
Devido a essas limitações, essa suíte de testes acabou sendo abandonada e uma nova ferramenta começou a ser estudada: Cypress. Essa ferramenta surgiu principalmente para lidar com as chamadas assíncronas, características das aplicações com client-side rendering, além de ser nativa em javascript, permitindo uma maior flexibilidade na hora de trabalhar com os elementos do front-end, tornando os testes mais robustos e evitando flacky test.
Neste post apresento como instalar o Cypress, rodar os testes e as principais features que permitem lidar melhor com o comportamento assíncrono, manutenibilidade e testes de features isoladas.
Instalação
O Cypress pode ser instalado facilmente através do terminal a partir da NPM.
npm install cypress
Com a instalação concluída, tudo que separa você de utilizar o framework é abrir o terminal de comando na pasta desejada e digitar o seguinte comando.
$(npm bin)/cypress open
Toda vez que você abrir o Cypress em uma pasta vazia, ele criará um projeto default com alguns exemplos, conforme figura 1.
fixtures: armazena os dados estáticos utilizados nos testes no formato json.
support: possui as funções e comandos customizados na suíte de testes.
integration: onde são criados os testes.
plugin: permite a utilização de plugins externos.
E externamente à pasta principal, um arquivo cypress.json também será criado onde permite definir as principais configurações do testes, como : url base, timeout,etc.
Além de abrir uma interface gráfica(fig. 2) onde mostra os testes disponíveis e permite rodar os testes.
Rodando os testes
Os testes podem ser rodados através da interface ou através do seguinte comando executado na pasta onde está localizado o arquivo cypress.json
$(npm bin)/cypress run
Principais features
Para demonstrar alguma das features e tornar mais claro, dois arquivos de testes foram desenvolvidos para uma aplicação web, desenvolvida com propósitos educacionais. Os testes e a aplicação estão disponíveis neste repositório do github. A aplicação consiste de uma página onde é possível cadastrar transações financeiras, importá-las e apaga-las.
O primeiro arquivo de teste realiza a inserção de transações, buscando demonstrar as funcionalidades mais básicas do framework: obter elementos e assertions. O segundo arquivo testa a funcionalidade de importação de transações e mostra como realizar testes isolados através do mock de requisições.
Teste de cadastro de transações
A execução deste teste está representada pelo gif 1 e possui o código da figura 3 e 4.
Criação de uma transação
Ao olhar o código, nota-se muitas familiaridades com outros frameworks.
Começando pela estrutura do teste, baseado em Mocha. Percebe-se também nos assertion styles que suportam BDD(expect/should) e TDD(assert).
Outro ponto importante, principalmente em relação ao comportamento assíncrono, é a Retry-Ability. Ao analisar o código de teste(fig.3) é possível notar que há basicamente dois tipos de ação: obter o elemento DOM e validar o parâmetro especificado. O Retry-Ability é a combinação deles, ou seja, primeiro captura um elemento, em sequência o valida. Mas então você deve estar se perguntando onde que está a mágica?
O Retry-Ability permite, caso a validação não passe, tentar obter o elemento novamente até que o timeout seja alcançado.
Criação de várias transações
Este teste explora outras funcionalidades do framework um pouco mais elaboradas, dentre elas: fixture(linha 38), funções customizadas(linha 57) e a utilização de closures(linha 56).
Fixture
Foi utilizado para armazenar as transações, permitindo cadastrar facilmente através da interação sobre os elementos do do JSON.
Closures
O Cypress não trabalha com atribuição de variáveis, exceto em poucos casos, sendo o acesso feito através de closures, pois garante o carregamento do elemento ao acessar o seu valor. Nota-se o seu uso ao acessar os valores da fixture.
Funções customizadas
Por último, na linha 60 foi utilizada a função verificaTabela. Essa função foi criada dentro da pasta support(Figura 5) ficando disponível para qualquer teste dentro deste projeto.
Percebe-se no código que é possível interagir diretamente com os elementos, como usar a propriedade length, aproveitando da vantagem do framework ser nativo em js. Essa interação ajuda a tornar o teste mais dinâmico e robusto.
Teste de importação de transações
O Cypress foi desenvolvido com o intuito para não apenas lidar com o comportamento assíncrono das aplicações client-side-rendering, mas também permitir testar cada feature de maneira mais isolada possível.
Por exemplo, testar a renderização das transações na tabela(gif 2). O pré-requisito é o cadastro dessas transações, podendo ser através da inserção manual ou importando através de uma requisição.
Ambas as alternativas utilizam outras partes do sistema, podendo interferir no teste. Portanto, como é possível testar de maneira mais isolada cada feature do sistema?
Neste caso o cypress permite mockar requisições xhr e sua resposta, tornando o teste mais isolado e possível, mesmo que a url requerida esteja indisponível.
A figura 4 apresenta o código do teste acima. Destaca-se o trecho da linha 22–27.
Para simular uma requisição(cy.route()) é necessário inicialmente inicializar o server(cy.server()). Além disso, também exige-se o tipo de requisição, endereço e a resposta desejada.
Portanto , toda vez que a requisição for requisitada pela aplicação, o mock será acionado e retornará a resposta desejada.
Conclusão
Após começar esse processo de mudança de framework, notei que os testes estão mais estáveis e há uma flexibilidade maior na criação dos testes. Contudo o início foi um pouco vagaroso, pois a estrutura dos testes e do próprio projeto difere bastante do padrão page Object utilizado anteriormente com o Selenium.
O Cypress oferta ainda várias funcionalidades não abordadas, mas espero com este post ter mostrado algumas das suas principais features, mostrando como você poderá realizar testes E2E e lidar com os comportamentos assíncronos da sua aplicação.
Segue o link do site oficial do Cypress e do repositório com os testes utilizados, respectivamente.