Testes E2E para SPAs JavaScript: Aguardando APIs e Renderização de Dados
Depois de concluirmos esta aula, nós:
- Entenderemos como escolher a condição de espera correta (wait condition) quando estamos escrevendo testes End-to-End para funcionalidades que buscam dados de uma API REST e os exibem na tela.
- Saberemos como adaptar nossos Page Objects (
TaskPage,TaskActions,TaskListPageeTaskListItem) para que todos os testes End-to-End existentes continuem passando sem alterar uma única linha nas classes de teste!
1. Escrevendo Testes End-to-End para Funcionalidades que Exibem Dados
Quando escrevemos testes End-to-End para uma funcionalidade que exibe dados em uma SPA, tipicamente queremos escrever dois tipos de testes:
- Queremos garantir que as informações corretas sejam exibidas para o usuário.
- Queremos verificar que os diferentes elementos HTML interativos (como links e botões) funcionam como esperado quando interagimos com eles.
Como vimos na aula anterior, em uma aplicação JavaScript (SPA) não podemos confiar que o documento HTML inteiro está pronto no instante em que navegamos para a URL da página: precisamos aguardar até que as chamadas de API REST terminem e o DOM seja construído pelo framework frontend. A grande pergunta é:
Como selecionamos a condição que deve ser verdadeira antes que o nosso teste End-to-End possa continuar?
Infelizmente, não existe uma resposta única e universal que sirva cegamente para todas as telas, e às vezes precisamos inspecionar o comportamento da página no navegador para encontrar a condição que elimina falhas intermitentes (flaky tests). Dito isso, uma excelente regra prática é:
- Aguardar até que a chamada de API mais lenta tenha terminado e seus resultados tenham sido renderizados no DOM, ou
- Aguardar até que possamos executar a operação interativa testada (como clicar em um link de ação que só aparece após o carregamento do componente).
Vamos analisar dois exemplos práticos da nossa aplicação para ver exatamente como aplicar esse raciocínio!
2. Renderizando um Único Item (TaskPage e TaskActions)
Quando escrevemos testes End-to-End para a funcionalidade que exibe os detalhes de uma única tarefa na SPA, queremos garantir que:
- As informações corretas da tarefa sejam exibidas para o usuário.
- O usuário possa excluir a tarefa exibida.
- O usuário possa navegar para a página de atualização da tarefa.
O template HTML da view que exibe os detalhes de uma única tarefa na nossa SPA possui a seguinte estrutura:
|
|
Qual condição devemos utilizar para aguardar até que o documento HTML dessa view tenha sido completamente construído?
Ao analisarmos o funcionamento dessa tela, notamos duas coisas:
- Buscar os dados da tarefa exibida (
GET /api/task/{id}) é a chamada de API mais lenta (e a única) que precisa terminar antes que a view exiba os dados da tarefa. - Não podemos executar todos os nossos testes End-to-End se os testes não puderem clicar nos links de excluir e atualizar a tarefa.
Ao inspecionarmos a página no DevTools do navegador, vemos que a view inteira é renderizada de uma só vez assim que a resposta JSON da tarefa retorna. Por isso, podemos aguardar até que nossos testes possam clicar no link de atualizar a tarefa (#update-task-link)!
Podemos implementar essa lógica de espera fazendo duas alterações na classe TaskPage:
- Adicionar um método
waitUntilPageIsOpen()à classeTaskPageque aguarda até que o elemento#update-task-linkseja clicável (SeleniumWait.waitUntilElementIsClickable(browser, By.id("update-task-link"))). - Atualizar o método
open()para que ele invoquewaitUntilPageIsOpen()logo apósbrowser.get(pageUrl)antes de retornar o novo objetoTaskPage.
Veja como fica a classe TaskPage com essas alterações:
|
|
Além disso, antes de podermos executar os testes End-to-End que clicam nos links da página de visualização da tarefa, precisamos atualizar o método updateTask() na classe TaskActions. Em vez de aguardar apenas a mudança de URL (que em uma SPA ocorre instantaneamente no cliente antes mesmo da API responder!), agora invocamos targetPage.waitUntilPageIsOpen() antes de retornar o UpdateTaskPage:
|
|
(Veremos a implementação de UpdateTaskPage.waitUntilPageIsOpen() na próxima aula, quando abordarmos formulários dinâmicos).
Vamos descobrir agora como implementar a lógica de espera para uma funcionalidade que renderiza uma lista de itens!
3. Renderizando uma Lista de Itens (TaskListPage e TaskListItem)
A view que renderiza a página de listagem de tarefas na nossa SPA possui três requisitos:
- Deve exibir uma mensagem informando quantas tarefas foram encontradas no banco de dados (
#task-list-size-message). - Deve exibir o título e o status traduzido de cada tarefa.
- Deve fornecer um link em cada tarefa que abre a página de visualização daquela tarefa.
O código do template principal da view de listagem é o seguinte:
|
|
E a tabela de tarefas propriamente dita é renderizada por um componente/diretiva cujo template HTML é o seguinte:
|
|
Quando olhamos para esses templates, notamos que buscar a lista de tarefas (GET /api/task) é a chamada de API mais lenta (e a única) que precisa terminar antes que a aplicação possa renderizar as linhas <tr class="task-list-item">. Portanto, a condição de espera ideal é aguardar até que todos os itens da lista (.task-list-item) estejam visíveis na interface de usuário!
Podemos implementar essa lógica de espera em três passos:
Passo 1: Adicionar waitUntilAllElementsAreVisible() ao SeleniumWait
Primeiro, adicionamos um novo método estático waitUntilAllElementsAreVisible() ao nosso objeto SeleniumWait, utilizando ExpectedConditions.visibilityOfAllElementsLocatedBy(elementLocator):
|
|
Passo 2: Atualizar a Classe TaskListPage
Segundo, fazemos duas alterações na classe TaskListPage:
- Adicionamos o método
waitUntilPageIsOpen(), que aguarda até que todos os elementos com a classe.task-list-itemestejam visíveis na tela. - Atualizamos o método
open()para invocarwaitUntilPageIsOpen()antes de retornar o novo objetoTaskListPage.
|
|
Passo 3: Atualizar o Método viewTask() na Classe TaskListItem
Terceiro, atualizamos o método viewTask() da classe TaskListItem para que, após clicar no link do título da tarefa, ele aguarde a página de detalhes da tarefa terminar de renderizar invocando targetPage.waitUntilPageIsOpen():
|
|
O Poder do Padrão Page Object: Com essas pequenas alterações encapsuladas exclusivamente dentro de
TaskPage,TaskActions,TaskListPageeTaskListItem, todas as nossas classes de teste End-to-End existentes (RenderViewTaskPageTesteRenderTaskListPageTest) passam a funcionar perfeitamente contra a SPA em JavaScript sem precisarmos alterar uma única linha de código nos testes!
Resumo
Esta aula nos ensinou três lições fundamentais:
- Em aplicações JavaScript (SPAs), não basta esperar a URL do navegador mudar (
ExpectedConditions.urlToBe); precisamos aguardar que os elementos do DOM dependentes da API REST sejam efetivamente renderizados. - Se quisermos garantir que os dados corretos sejam exibidos ao usuário (como em uma tabela ou lista), devemos aguardar até que a chamada de API mais lenta termine e todos os itens sejam renderizados (
waitUntilAllElementsAreVisible). - Se quisermos verificar que o usuário pode realizar uma ação na página, devemos aguardar até que o elemento de ação correspondente esteja clicável (
waitUntilElementIsClickable).