Testes e2e em React com Cypress

Eduardo Henrique Gris - Oct 28 - - Dev Community

Introdução

No artigo do mês de setembro, escrevi sobre como configurar Cypress para ser possível realizar testes end-to-end em React e mostrei duas formas de executar eles (via interface e via terminal). A ideia agora é mostrar como funciona a realização dos testes, com alguns conceitos e exemplos.

Estrutura testes

A estrutura dos testes segue da seguinte forma:

  • describe: vai representar o bloco de testes a ser executado, podendo ser referente a um contexto ou fluxo específico
  • it: vai representar o teste que vai ser executado, que vai seguir as seguintes etapas
    1. acessar a página onde vai começar o teste
    2. buscar um elemento HTML dentro da tela
    3. interagir com elemento buscado
    4. definir uma expectativa para o teste
describe("Descrição bloco", () => {
  it("descrição teste", () => {
    acesso da página
    busca de elemento na tela
    interação com elemento

    expectativa do teste
  })

  it("descrição outro teste", () => {
    (…)
  })
})
Enter fullscreen mode Exit fullscreen mode

Aqui está exemplificado uma busca, uma interação de elemento e uma expectativa no teste, mas é possível realizar múltiplas dentro dele.

Acesso da página

Para acessar uma página será usado cy.visit(), que permite acessar uma url, que vai ser o ponto de partida dos testes.

Busca de elemento na tela

Para a busca de elementos HTML existem diferentes formas disponíveis, seguem alguns exemplos:

Query Busca Código
contains pelo texto contains(text)
get pelo seletor get(selector)

A busca por get pode ser realizada de diversas formas:

Busca Exemplo
pelo data-cy cy.get('[data-cy="example"]')
pelo name cy.get('[name="submit"]')
pelo id cy.get('#main')
pelo className cy.get('.btn')
pela role (função) cy.get('button')

O data-cy corresponde a uma definição no elemento exclusiva para localizá-lo nos testes.

Interação com elemento

Após a busca do elemento na página é possível interagir com ele, seguem algumas formas disponíveis:

Interação Ação
click() clique
dblclick() clique duplo
type() escrita de texto
check() seleciona checkbox/radio button
uncheck() deseleciona checkbox
submit() submeter um form

Expectativa do teste

Para definir a expectativa do teste será usado should(), que possui alias and(), que permite fazer a validação dos testes, seguem algumas formas disponíveis:

Expectativa Validação
should('have.value', value) valor
should('be.enabled') está habilitado
should('be.disabled') está desabilitado
should('contain', text) texto
should('have.length', length) tamanho
should('exist') existência
should('include') inclusão

Para validar a negação das expectativas seria colocar not. como primeira parte dentro do should, por exemplo should('not.exist').

Execução dos testes

No artigo do mês passado, que o link está na introdução desse artigo, foram definidas duas formas de executar os testes em package.json, pela interface e pelo terminal:

{
  "scripts": {
    //...
    "cy:open": "cypress open", // interface
    "cy:run": "cypress run" // terminal
  }
}
Enter fullscreen mode Exit fullscreen mode

Para execução dos testes localmente em Cypress, é necessário primeiro executar a aplicação localmente em um terminal, depois executar os testes em outro terminal.
Sendo uma mesma porta definida quando se sobe a app local e para evitar sempre escrever o mesmo começo na primeira etapa de todos os testes (partindo por exemplo da porta 3000), é possível definir em cypress.config.js:

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  e2e: {
    baseUrl: 'http://localhost:3000',
  },
})
Enter fullscreen mode Exit fullscreen mode

Dessa forma todo teste que começar com cy.visit('/') estará partindo da página http://localhost:3000/.
Observação: no caso de executar em pipelines de ambientes de teste, seria colocar uma ENV em baseUrl para acessar a url esperada relacionada ao ambiente que for rodado os testes.

Exemplos de testes

Para os exemplos apresentados será usada a página de exemplo que o próprio Cypress disponibiliza: https://example.cypress.io
Que vai representar, para o artigo aqui, como se fosse a url referente a aplicação rodando no terminal localmente.
Como todos os exemplos vão partir dessa url, em cypress.config.js será colocada ela como baseUrl:

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  e2e: {
    baseUrl: 'https://example.cypress.io',
  },
})
Enter fullscreen mode Exit fullscreen mode

A ideia é trazer exemplos mais simples para mostrar um pouco de cada etapa que compõe a escrita dos testes, mas na prática o Cypress quando é utilizado para validação de testes de ponta a ponta, os testes costumam ser mais complexos e terem bem mais interações dentro deles.

Teste de presença

Nesse primeiro teste vai ser analisado após clicar em um link na página de exemplo do Cypress, se chegou na página esperada (fazendo uma expectativa em cima da url) e se tem alguns elementos presentes na tela.
Primeiro vai ser colocado o arquivo dentro da pasta cypress/e2e, que é onde o Cypress vai buscar os testes para serem executados, definindo o arquivo presence.cy.js:

describe('Presence tests', () => {
  beforeEach(() => {
    cy.visit('/')

    cy.contains('get').click()
  })

  it('reach expected page', () => {
    cy.url().should('include', '/commands/querying')
  })

  it('has queries options', () => {
    cy.get('#querying').should('contain', 'get()')
                       .and('contain', 'contains()')
                       .and('contain', 'within()')
                       .and('contain', 'root()')
  })
})
Enter fullscreen mode Exit fullscreen mode

Para esse bloco de teste está se começando acessando a página inicial de exemplo do Cypress, buscando o link que possui texto get e clicando nele. Como todos os testes do bloco tem o mesmo começo, essa parte foi colocada dentro de um beforeEach antes dos testes.
Se espera no primeiro teste que a página que chegar inclua na url /commands/querying e no segundo teste que se tenha na tela as diferentes queries disponíveis, validando pelo texto delas.

Executando no terminal yarn cy:run, retorna as informações do resultado dos testes que vou quebrar em pedaços para explicar abaixo.
Primeiro é mostrado as informações da execução, com versão do Cypress utilizada, o browser, a versão do node, a quantidade de arquivos de teste e onde foram buscados:

Image description

Depois é mostrado o arquivo que foi executado, o bloco de testes e os testes que foram realizados (com um positivo do lado mostrando que passou o teste), com a quantidade dos que passaram:

Image description

Após é mostrado um caixa com os resultados dos testes, com a quantidade de testes executados, quantos passaram, quantos falharam, quantos estão pendentes, quantos foram dado skip, quantos geraram screenshots, se foi gerado vídeo, a duração dos testes e por fim o arquivo executado:

Image description

E por fim, uma tabela com o arquivo executado, tempo de execução, quantidades de testes, quantos passaram, quantos falharam, quantos estão pendentes e quantos foram dado skip. Como todos os testes passaram, vem uma mensagem All specs passed!:

Image description

Uma outra forma de executar os testes é via interface, seguindo os seguintes passos:

  • Executar yarn cy:open
  • Escolher a caixa E2E Testing
  • Escolher o navegador de preferência
  • Clicar em Start E2E Testing
  • Clicar no arquivo que deseja executar
  • Aguardar a execução dos testes

Image description

Durante a execução será mostrado do lado direito a navegação acontecendo de acordo com os testes.
Finalizada a execução, do lado esquerdo aparecerá o bloco que foi executado e os testes, como nesse caso eles passaram vai ter um positivo do lado deles. Do lado direito onde parou ao final das execuções:

Image description

Do lado esquerdo é possível abrir um dos testes e passar o mouse em cada parte que executou dentro do teste e ver do lado direito onde estava durante a navegação:

Image description

Agora simulando uma falha no segundo teste do bloco, modificando uma das expectativas, rodando pelo terminal terá algumas diferenças na visualização do resultado.
Será mostrado o teste dentro do bloco que falhou (destacado em vermelho), que um teste passou e outro teve falha, e onde especificamente a falha aconteceu:

Image description

A parte de resultados vai estar destacado em vermelho, informando que um teste falhou e que foi criado um screenshot mostrando como estava a tela no momento da falha:

Image description

E por fim será mostrado em uma tabela que de dois testes, um passou e um falhou. Com o destaque no final em vermelho que 1 of 1 failed (100%). Essa informação corresponde a quantos blocos de testes falharam, não os testes em si. Como o bloco Presence tests teve um teste falhando dentro dele, isso já faz o bloco ser considerado com falha:

Image description

No caso da execução via interface, vai ser mostrado onde especificamente o teste falhou com a imagem ao lado:

Image description

Teste múltiplas interações

Nesse teste vai ser analisado após clicar no link type na página de exemplo do Cypress, se chegou na página esperada (fazendo uma expectativa em cima da url) e após escrita de texto em um campo input se o valor dentro dele corresponde ao que foi escrito.
Vai ser colocado o arquivo dentro da pasta cypress/e2e, que é onde o Cypress vai buscar os testes para serem executados, definindo o arquivo actions.cy.js:

describe('Actions tests', () => {
  beforeEach(() => {
    cy.visit('/')

    cy.contains('type').click()
  })

  it('reach expected page', () => {
    cy.url().should('include', '/commands/actions')
  })

  it('fill email address', () => {
    cy.contains('Email address').next().type('example@aloha.com')

    cy.contains('Email address').next().should('have.value', 'example@aloha.com')
  })
})
Enter fullscreen mode Exit fullscreen mode

Nesse bloco de testes está sendo usado next(), que acessa o elemento html que está logo após ao que foi buscado. Dessa forma, é possível acessar o campo input para preencher, que está logo após o título buscado.
Após a execução, os testes passam com sucesso.

Boas práticas

A documentação do Cypress apresenta boas prática a serem seguidas durante os testes, vou apresentar algumas abaixo:

Definir um baseUrl global

Para testes que sempre começam pelo mesmo domínio, definir em cypress.config.js a baseUrl:

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  e2e: {
    baseUrl:,
  },
})
Enter fullscreen mode Exit fullscreen mode

Usar beforeEach para códigos compartilhados entre os testes

É recomendado colocar código compartilhado dentro de um beforeEach:

beforeEach(() => {
// código compartilhado
})
Enter fullscreen mode Exit fullscreen mode

Realizar múltiplas expectativas em conjunto

Ao invés de separar expectativas em diferentes testes para um mesmo elemento, fazer expectativas múltiplas:

// Não recomendado
it('has get query option', () => {
  cy.get('[data-cy="queries"]').should('contain', 'get()')
})

it('has within query option', () => {
  cy.get('[data-cy="queries"]').should('contain', 'within()')
})
Enter fullscreen mode Exit fullscreen mode
// Recomendado
it('has queries options', () => {
  cy.get('[data-cy="queries"]').should('contain', 'get()')
                               .and('contain', 'within()')
})
Enter fullscreen mode Exit fullscreen mode

Prioridade de busca de elementos

Esse pode ser um pouco polêmico, dado que a principal sugestão é adicionar um elemento exclusivo de localização de teste dentro do elemento HTML. Partindo do seguinte componente:

<button
  id="example"
  class="btn-primary"
  name="submission"
  role="button"
  data-cy="primary"
>
  Submit
</button>
Enter fullscreen mode Exit fullscreen mode

Segue abaixo a ordem crescente de prioridade e os motivos apresentados na documentação:

Seletor Recomendado Motivo
cy.get('button').click() Nunca O pior. Muito genérico, sem contexto
cy.get('.btn-primary').click() Nunca Ruim. Acoplado com estilização e com grande probabilidade de mudança
cy.get('#example').click() Com moderação Melhor. Mas ainda acoplado com estilização e event listeners em JS
cy.get('[name="submission"]').click() Com moderação Acoplado com attributo name, que tem semântica HTML
cy.contains('Submit').click() Depende Muito melhor. Mas ainda acoplado a texto que pode ser modificado
cy.get('[data-cy="primary"]').click() Sempre O Melhor. Isolado de mudanças

Conclusão

A ideia desse artigo foi trazer uma visão geral de como funciona os testes, abordando estrutura, acesso de página, busca de elemento HTML, interações e validações, com apresentação de exemplos e boas práticas que a documentação recomenda.
Mas são possíveis vários outros cenários de testes e formas de uso do Cypress, por essa razão estou colocando os principais links separados por tema para quem quiser se aprofundar mais.

Links

Escrita e organização de testes
Etapas execução testes
Boas práticas
Comandos
Acesso url
Busca usando get
Interações
Expectativas
Execução testes

. . . . . . . . . . . . . . . . . . . .
Terabox Video Player