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
- acessar a página onde vai começar o teste
- buscar um elemento HTML dentro da tela
- interagir com elemento buscado
- 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", () => {
(…)
})
})
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
}
}
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',
},
})
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',
},
})
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()')
})
})
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:
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:
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:
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!
:
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
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:
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:
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:
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:
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:
No caso da execução via interface, vai ser mostrado onde especificamente o teste falhou com a imagem ao lado:
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')
})
})
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:,
},
})
Usar beforeEach
para códigos compartilhados entre os testes
É recomendado colocar código compartilhado dentro de um beforeEach
:
beforeEach(() => {
// código compartilhado
})
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()')
})
// Recomendado
it('has queries options', () => {
cy.get('[data-cy="queries"]').should('contain', 'get()')
.and('contain', 'within()')
})
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>
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