Português (Brasil)
  • teste
  • teste de ponta a ponta
  • teste de integração
  • desenvolvimento
  • produtividade
  • jest
  • puppeteer

Um guia rápido para escrever testes de ponta a ponta com jest-puppeteer

Este artigo fornece um guia rápido para escrever testes de ponta a ponta eficientes com jest-puppeteer, enfatizando o processo de configuração, APIs comumente usadas e cenários de testes práticos usando um aplicativo de lista de tarefas simples como exemplo.

Yijun
Yijun
Developer

Como parte do nosso compromisso em garantir a qualidade do Logto e melhorar continuamente, utilizamos o jest-puppeteer para testes automatizados de ponta a ponta. Isso nos permite iterar rapidamente no desenvolvimento do Logto sem interrupções.

Neste artigo, compartilharemos nossa experiência em escrever scripts de teste eficientes com jest-puppeteer usando um aplicativo de lista de tarefas simples como exemplo. Nosso objetivo é ajudar você a começar rapidamente a escrever códigos de teste com jest-puppeteer para seus próprios projetos.

Introdução ao teste de ponta a ponta com jest-puppeteer

O teste de ponta a ponta é uma forma de garantir que sua aplicação funcione corretamente do ponto de vista do usuário. Para atingir esse objetivo, utilizamos duas ferramentas essenciais: Jest e Puppeteer.

Jest é um popular framework de testes em JavaScript que oferece uma API amigável para escrever testes e asserções. É amplamente utilizado para testes unitários e de integração.

Puppeteer é uma biblioteca Node.js desenvolvida pela equipe do Chrome que fornece uma API de alto nível para controlar navegadores Chrome ou Chromium sem cabeça. Isso o torna uma escolha ideal para automatizar interações de navegador em testes de ponta a ponta.

jest-puppeteer é uma configuração pré-definida do Jest que habilita o teste de ponta a ponta com Puppeteer. Ele oferece uma API simples para lançar novas instâncias de navegador e interagir com páginas da web por meio delas.

Agora que você tem uma compreensão básica das ferramentas essenciais, vamos mergulhar na configuração do seu ambiente de teste.

Configurando seu ambiente de teste

Configurar seu ambiente de teste para testes de ponta a ponta com jest-puppeteer é um processo simples que envolve três etapas principais:

  1. Instalar dependências

No seu projeto (ou criando um novo projeto), abra o terminal e execute:

Depois vá para o caminho node_modules/puppeteer e instale o Chromium (que é necessário para o Puppeteer):

  1. Configurar o Jest

Em seguida, você precisa configurar o Jest para funcionar perfeitamente com o Puppeteer.

Crie um arquivo de configuração do Jest (por exemplo, jest.config.js) no diretório raiz do seu projeto, se ainda não tiver um. Nesse arquivo de configuração, especifique jest-puppeteer como preset:

Você pode personalizar outras configurações do Jest neste arquivo, conforme necessário. Para mais informações sobre como personalizar as configurações do Jest, consulte a configuração do Jest.

  1. Escreva seus testes

Crie arquivos de teste no seu projeto, normalmente com a extensão .test.js. O Jest descobrirá automaticamente e executará esses arquivos de teste.

Aqui está um exemplo da documentação do Jest:

Em seguida, execute o comando para rodar o teste:

Seguindo essas três etapas, você terá um ambiente de teste bem configurado para realizar testes de ponta a ponta usando jest-puppeteer.

No entanto, note que este é apenas um exemplo básico. Para informações mais detalhadas sobre a configuração do ambiente, consulte a documentação relevante:

APIs comumente usadas

Nos próximos passos, faremos uso das APIs fornecidas pelo Jest, Puppeteer e jest-puppeteer para auxiliar em nossos testes.

O Jest oferece principalmente APIs para organizar testes e asserir resultados esperados. Você pode explorar os detalhes específicos na documentação.

As APIs do Puppeteer são projetadas principalmente para interagir com navegadores, e seu suporte para testes pode não ser tão direto. Você pode consultar a documentação do Puppeteer para entender as funcionalidades que ele oferece. Nos exemplos de testes subsequentes, abordaremos alguns casos de uso comuns.

Como as APIs do Puppeteer não foram originalmente projetadas para testes, usá-las para escrever testes pode ser desafiador. Para simplificar o processo de escrever testes com Puppeteer, jest-puppeteer inclui uma biblioteca embutida chamada expect-puppeteer. Ela oferece um conjunto de APIs concisas e amigáveis. A biblioteca é detalhada em sua documentação, que apresenta vários recursos úteis, como mostrado nos exemplos abaixo:

Nos exemplos seguintes, combinaremos as APIs fornecidas por essas bibliotecas para completar nossos cenários de teste.

Agora é hora de começar a aprender como escrever código de teste usando nosso simples aplicativo de lista de tarefas.

O simples aplicativo de lista de tarefas

Supondo que temos um simples aplicativo de lista de tarefas rodando em http://localhost:3000, o código HTML principal deste aplicativo é o seguinte:

Ao realizar testes de ponta a ponta, pretendemos cobrir os seguintes cenários:

  1. Ao acessar a URL do aplicativo, o aplicativo e seus dados devem carregar corretamente.
  2. O botão de verificação do item deve ser capaz de ser clicado.
  3. Clicar em um link externo nas notas do item deve abrir o link em uma nova aba.
  4. Deve ser possível adicionar um item a partir do formulário.

Mais tarde, escreveremos código de teste para validar esses cenários.

Esperar que o aplicativo e seus dados tenham sido carregados

Consideramos que o aplicativo foi carregado com sucesso quando as seguintes condições são atendidas:

  1. Um elemento com o ID "app" deve existir na página após acessar a URL do aplicativo.
  2. Os dados dentro do aplicativo devem ser renderizados corretamente.

Então, escrevemos o seguinte código de teste:

No código:

  • page.goto é equivalente a digitar "http://localhost:3000" no navegador, permitindo que você navegue para qualquer URL.
  • page.waitForSelector é usado para esperar que um seletor CSS específico corresponda a um elemento em particular, com um tempo de espera padrão de 30 segundos.
  • expect(page).toMatchElement é da biblioteca expect-puppeteer. É similar a page.waitForSelector, mas também suporta corresponder ao texto dentro de um elemento. Além disso, o tempo de espera padrão para toMatchElement é de apenas 500ms.

Pode parecer perfeito à primeira vista, mas ao rodar este teste e implantá-lo em um ambiente de CI, ele ocasionalmente falha após múltiplas execuções. A mensagem de falha indica:

Com base na informação de falha e no fato de que este teste passa na maioria das vezes, podemos inferir que os dados solicitados pelo aplicativo podem não retornar dentro dos primeiros 500ms após o carregamento do aplicativo. Portanto, queremos esperar que os dados sejam carregados antes de fazer a asserção.

Normalmente, existem duas abordagens comuns para alcançar isso:

  1. Aumentar o tempo de espera da asserção

A partir da mensagem de erro, podemos ver que o tempo de espera padrão para toMatchElement é definido em 500ms. Podemos aumentar o tempo de espera adicionando a opção timeout à função desta forma:

Essa abordagem pode reduzir a ocorrência de falhas nos testes até certo ponto, mas não resolve completamente o problema porque não podemos saber com certeza quanto tempo é necessário para que os dados sejam buscados.

Portanto, só usamos esta abordagem quando temos certeza sobre o tempo de espera necessário, como em cenários como "um tooltip aparece após ficar sobre um elemento por mais de 2 segundos".

  1. Esperar a conclusão da requisição de rede antes de fazer uma asserção

Essa é a abordagem correta. Embora possamos não saber quanto tempo a requisição de dados levará, esperar a conclusão da requisição de rede é sempre uma escolha segura. Neste ponto, podemos usar page.waitForNavigation({ waitUntil: 'networkidle0' }) para esperar que as requisições de rede sejam concluídas:

Dessa forma, antes de executarmos a asserção para o aplicativo e seus dados carregados, podemos garantir que nossas requisições de rede já foram concluídas. Isso assegura que o teste produza resultados corretos de forma consistente.

Esperar clicar em um botão específico

Em seguida, estamos testando a funcionalidade de clicar no botão de verificação dentro de um item.

No aplicativo de exemplo, percebemos que todos os itens compartilham a mesma estrutura. Seus botões de verificação têm o seletor CSS li[class$=item] > button, e o texto do botão é sempre "Verificar". Isso significa que não podemos especificar diretamente em qual botão de verificação de item clicar. Portanto, precisamos de uma nova solução.

Suponha que queremos clicar no botão de verificação do item "Ler o documento de introdução do Logto". Podemos dividir isso em duas etapas:

  1. Primeiro, obter uma referência para aquele item específico.
  2. Em seguida, clicar no botão de verificação localizado dentro desse item.

Como mostrado no código, usamos a função toMatchElement para corresponder ao item com itemName definido como "Ler o documento de introdução do Logto". Em seguida, usamos a referência lerDocItem para clicar no botão com o conteúdo de texto 'Verificar' localizado abaixo dele.

É importante notar que ao obter o lerDocItem, o seletor CSS usado é li[class$=item]:has(div[class$=itemName]). Este seletor corresponde ao elemento raiz li do item, não ao itemName dentro dele, porque pretendemos clicar no button sob a tag li mais tarde.

O uso de expect(lerDocItem).toClick é semelhante ao toMatchElement. No código de exemplo, passamos { text: 'Verificar' } para corresponder ainda mais ao textContent do botão. No entanto, você pode escolher se deseja ou não corresponder ao conteúdo de texto do botão com base em suas necessidades.

Em seguida, queremos testar se conseguimos abrir um link externo em uma nova aba ao clicar nele nas notas do item.

No aplicativo de exemplo, descobrimos que o item "Ler o documento de introdução do Logto" possui um link externo para a documentação do Logto dentro de suas notas. Aqui está nosso código de teste:

No código, utilizamos toClick para clicar no link dentro de itemNotes.

Subsequentemente, usamos browser.waitForTarget para capturar uma nova aba aberta com a URL "https://docs.logto.io/docs/tutorials/get-started".

Depois de obter a aba, usamos target.page() para obter uma instância da página da documentação do Logto e verificar se a página foi carregada.

Lembre-se também de fechar a nova página aberta após concluir nosso teste.

Esperar criar um item a partir do formulário

Agora, queremos testar a funcionalidade de adicionar um item.

Precisamos preencher o nome do item e as notas do item no formulário, clicar no botão "Adicionar" e verificar se o conteúdo que adicionamos aparece na lista:

Você perceberá que usamos a função expect(page).toFill(inputSelector, content) para preencher o conteúdo do formulário. Essa função substitui todo o conteúdo no input por content.

Se você quiser adicionar alguns caracteres ao input sem substituir o conteúdo existente, pode usar page.type(selector, content) para adicionar diretamente o conteúdo desejado ao input.

No entanto, quando temos muitos campos para preencher em nosso formulário, chamar toFill várias vezes não é conveniente. Nesse caso, podemos usar a seguinte abordagem para preencher todo o conteúdo em uma única chamada:

A chave do objeto fornecido é o atributo name do elemento input.

Resumo

Cobrimos um exemplo simples para apresentar requisitos de teste comuns e técnicas correspondentes de escrita de código ao usar jest-puppeteer para testes de ponta a ponta. Esperamos que isso possa ajudá-lo a entender rapidamente os fundamentos da escrita de código de teste com jest-puppeteer.