Testes E2E para SPAs JavaScript: Formulários Dinâmicos
Depois de concluirmos esta aula, nós:
- Entenderemos como selecionar a condição de espera correta (wait condition) quando estamos escrevendo testes End-to-End para funcionalidades que criam ou atualizam dados por meio de formulários dinâmicos em uma SPA.
- Saberemos como fazer as alterações necessárias nos nossos Page Objects de formulário (
UpdateTaskPageeTaskForm).
1. Escrevendo Testes End-to-End para Formulários em SPAs
Quando estamos escrevendo testes End-to-End para uma funcionalidade que exibe e submete um formulário em uma aplicação web JavaScript (SPA), tipicamente queremos escrever dois tipos de testes:
- Queremos garantir que os campos do formulário sejam pré-populados com os dados corretos (no caso de um formulário de edição/atualização).
- Queremos verificar que o usuário consegue preencher e submeter o formulário testado e fazer asserções sobre o resultado esperado.
Mais uma vez, como estamos testando uma SPA, não podemos confiar que o formulário e seus dados já estão prontos no DOM assim que abrimos a URL da página. Como escolhemos a condição de espera que deve ser verdadeira antes que o nosso teste End-to-End possa prosseguir?
Temos duas opções principais:
- Aguardar até que a chamada de API mais lenta tenha terminado e seus resultados tenham sido renderizados: Essa é uma excelente escolha se o nosso formulário possuir longas listas dropdown (
<select>) ou autocompletes que são populados assincronamente com dados buscados do banco de dados. - Aguardar até que o nosso caso de teste possa interagir com o formulário: Essa é a escolha ideal quando estamos escrevendo testes End-to-End para um formulário simples (como o formulário de tarefas) que só é exibido no DOM (
ng-show="task"/ renderização condicional) depois que os dados da entidade foram carregados da API REST!
Vamos ver um exemplo prático que demonstra como implementar essa lógica de espera para a funcionalidade de atualização de uma tarefa.
2. Atualizando as Informações de uma Tarefa (UpdateTaskPage e TaskForm)
Quando escrevemos testes End-to-End para a funcionalidade que atualiza os dados de uma tarefa existente, queremos garantir que:
- Os campos do formulário de edição sejam preenchidos com os valores atuais da tarefa encontrada no banco de dados.
- O usuário consiga alterar e salvar as novas informações da tarefa.
O código-fonte do template HTML da nossa SPA que renderiza o formulário de tarefa (#task-form) é o seguinte:
|
|
Observe um detalhe importante nesse template:
- O elemento
<form id="task-form">possui o atributong-show="task", o que significa que o formulário permanece oculto até que a requisiçãoGET /api/task/{id}termine e popule o objetotask! - Além disso, o botão
#submit-task-formcomeça desabilitado (ng-disabled="taskForm.$invalid || !taskForm.$dirty") até que o usuário modifique algum campo do formulário! Portanto, não podemos esperar que#submit-task-formseja clicável ao abrir a página, pois ele estará desabilitado até digitarmos algo. - Em vez disso, como nossos testes precisam ler ou editar o campo de título (
#task-title), a condição de espera perfeita ao abrir a página do formulário é aguardar até que o campo de título (#task-title) esteja visível e clicável!
Podemos implementar essa condição de espera em dois passos:
Passo 1: Atualizar a Classe UpdateTaskPage
Primeiro, fazemos duas alterações na classe UpdateTaskPage:
- Adicionamos o método
waitUntilPageIsOpen()à classeUpdateTaskPage, implementando-o para aguardar até que o campo#task-titleseja clicável (SeleniumWait.waitUntilElementIsClickable(browser, By.id("task-title"))). - Atualizamos o método
open()para que ele invoquewaitUntilPageIsOpen()antes de retornar o novo objetoUpdateTaskPage.
Depois de fazermos essas alterações, o código-fonte da classe UpdateTaskPage fica assim:
|
|
(De maneira idêntica, na classe CreateTaskPage, também invocamos waitUntilPageIsOpen() aguardando que #task-title esteja clicável ao abrir /task/create).
Passo 2: Atualizar a Classe TaskForm
Segundo, fazemos duas alterações na classe TaskForm:
- Simplificamos o construtor da classe
TaskForm: Como na nossa SPA tanto o formulário de criação quanto o de atualização são renderizados pelo mesmo componente reutilizável, oiddo elemento<form>é sempre"task-form". Por isso, podemos remover o parâmetroformIddo construtor e buscar diretamenteBy.id("task-form"). - Atualizamos a implementação do método
submitTaskForm(): Em vez de chamarform.submit()(que faria um POST síncrono tradicional), nossa nova implementação clica no botão#submit-task-form(form.findElement(By.id("submit-task-form")).click()) e aguarda até que a página de visualização da tarefa (TaskPage) esteja aberta (shown.waitUntilPageIsOpen()) antes de retornar o novo objetoTaskPage!
Depois de fazermos essas alterações, o código-fonte completo da classe TaskForm fica assim:
|
|
Mais uma vez, observe que não precisamos fazer nenhuma alteração nas nossas classes de teste End-to-End (SubmitCreateTaskFormWithValidInformationTest e SubmitUpdateTaskFormWithValidInformationTest)! Toda a complexidade de sincronização assíncrona com a SPA ficou 100% encapsulada dentro de UpdateTaskPage, CreateTaskPage e TaskForm.
Resumo
Esta aula nos ensinou duas regras práticas essenciais para testar formulários em SPAs:
- Se o nosso formulário possuir longas listas dropdown (
<select>) populadas assincronamente com dados vindos do banco de dados, é uma boa ideia aguardar até que essas listas tenham sido populadas. - Se estivermos escrevendo testes End-to-End para um formulário simples (cujo botão de submit pode iniciar desabilitado até que o formulário seja modificado), a melhor estratégia é aguardar até que nossos testes possam interagir com o primeiro campo de entrada do formulário (
waitUntilElementIsClickable(browser, By.id("task-title"))).