Especificações E2E com Kotest: Formulários e Validação
Depois de concluirmos esta aula, nós:
- Saberemos escrever testes End-to-End com Kotest e Playwright para funcionalidades que submetem um formulário.
- Compreenderemos como devemos estruturar nossas classes de especificação quando especificamos o comportamento do sistema sob especificação.
Esta aula assume que:
- Você sabe executar seus testes End-to-End utilizando o Gradle 9 (
endToEndTest).- Você está familiarizado com o Playwright e o padrão Page Object.
- Você sabe escrever testes End-to-End para aplicações web Spring utilizando JUnit 6.
- Você está familiarizado com o Kotest Framework.
- Você sabe configurar seus testes End-to-End no Spring Boot 4 com
PlaywrightExtensione@Sql.- Você sabe escrever testes End-to-End no Kotest para funcionalidades que renderizam dados.
Nota importante: Você deve ter concluído a aula Escrevendo Testes End-to-End para Aplicações Web Spring — Submetendo um Formulário antes de prosseguir com esta aula, pois ela explica os Page Objects
CreateTaskPageeCreateTaskForm. O objetivo desta aula é demonstrar como escrever especificações End-to-End para formulários usando o Kotest Framework.
Especificando o Comportamento da Funcionalidade de Criação de Tarefa (Create Task Feature)
Podemos especificar o comportamento esperado da funcionalidade de criação de tarefa seguindo estes seis passos:
Primeiro, temos que criar uma nova classe de especificação (CreateTaskSpec) e configurar o servidor Spring Boot, os scripts @Sql e a PlaywrightExtension. Após termos feito isso, o código-fonte da nossa classe de especificação fica assim:
|
|
Segundo, temos que adicionar duas constantes privadas à nossa classe de especificação. Essas constantes contêm o título (TITLE) e a descrição (DESCRIPTION) da tarefa criada:
|
|
Nota: Adicionamos essas constantes diretamente ao arquivo da nossa classe de especificação porque elas não são utilizadas por outras classes de especificação.
Terceiro, temos que adicionar uma variável createTaskPage do tipo CreateTaskPage à nossa classe de especificação. Nosso cenário utiliza esse Page Object quando abre a página de criação de tarefa e submete o formulário de criação de tarefa.
Quarto, inicializamos createTaskPage antes do cenário raiz (beforeContainer) instanciando CreateTaskPage(playwright.page).
Quinto, garantimos que o usuário autenticado seja desconectado (NavigationBar(playwright.page).logUserOut()) após a execução do cenário raiz (afterContainer).
Sexto, temos que escrever o cenário que especifica o comportamento da funcionalidade de criação de tarefa quando um usuário registrado cria uma nova tarefa usando informações válidas. Podemos escrever este cenário seguindo estes passos:
- Adicionar um novo bloco
Given/When/Thenà nossa classe de especificação. - Abrir a página de login (
LoginPage(playwright.page).open()) e autenticar o usuário (JohnDoe.EMAIL_ADDRESS,JohnDoe.PASSWORD). - Abrir a página de criação de tarefa (
createTaskPage.open().getForm()), preencher o título (typeTitle(TITLE)) e a descrição (typeDescription(DESCRIPTION)) da tarefa criada no formulário, submeter o formulário (submitTaskForm()) e armazenar o Page Object retornado (TaskPage) na variávelshownPage. - Garantir que o sistema sob especificação abriu a página de visualização da tarefa recém-criada (
shownPage.isOpenWithUnknownTaskId() shouldBe true). - Verificar se a página de visualização exibe o título da tarefa criada (
TITLE). - Garantir que a página de visualização exiba a descrição da tarefa criada (
DESCRIPTION). - Verificar se a página de visualização não exibe o nome do responsável (
lifecycleFields.isAssigneeNameVisible() shouldBe false). - Garantir que a página de visualização exiba o nome da pessoa que criou a tarefa (
JohnDoe.NAME). - Verificar se a página de visualização exibe o nome da pessoa que modificou a tarefa (
JohnDoe.NAME). - Garantir que a página de visualização não exiba os campos de ciclo de vida de uma tarefa fechada (
lifecycleFields.areClosedTaskFieldsVisible() shouldBe false).
Após termos escrito este cenário, o código-fonte completo de CreateTaskSpec fica assim:
|
|
Agora sabemos escrever testes End-to-End para funcionalidades que submetem um formulário. A seguir, veremos como devemos estruturar nossas classes de especificação.
Estruturando Nossas Classes de Especificação
É tecnicamente possível escrever um único cenário longo que especifique tanto o comportamento do sistema quando a validação falha quanto quando a validação é bem-sucedida. Dito isso, devemos responder a duas perguntas antes de decidir usar essa abordagem:
- Nosso cenário fica longo demais? Como teríamos que encadear múltiplos passos de erro e sucesso no navegador, o cenário pode ficar extenso e difícil de ler.
- É trabalhoso demais fornecer um estado inicial limpo para cada caso? Em funcionalidades complexas, preparar o estado da tela entre uma submissão inválida e uma válida pode tornar o teste frágil.
Quando refletimos sobre essas duas questões, fica claro que na maioria das vezes devemos estruturar nossas classes de especificação seguindo estas regras:
- Devemos criar uma classe de especificação que contenha os cenários que especificam o comportamento do sistema sob especificação quando a validação falha.
- Devemos criar uma classe de especificação que contenha os cenários que especificam o comportamento do sistema sob especificação quando a validação é bem-sucedida.
- Devemos escrever um cenário por papel de usuário (user role). Isso ajuda a minimizar o número de classes de especificação e produz relatórios HTML limpos e lógicos.
Nota sobre Estratégia de Testes:
Não precisamos necessariamente criar uma classe de especificação End-to-End para testar falhas de validação de formulário! Essa decisão depende da nossa estratégia de testes: se já escrevemos testes unitários e testes de integração (
MockMvc) que garantem que o controlador e o Bean Validation funcionam conforme o esperado quando a validação falha, escrever testes End-to-End no navegador para esse mesmo cenário geralmente não compensa o custo de manutenção e tempo de execução.
Vamos resumir o que aprendemos nesta aula.
Resumo
Esta aula nos ensinou três coisas:
- Caso decidamos testar falhas de validação no nível E2E, devemos criar uma classe de especificação separada para quando a validação falha.
- Devemos criar uma classe de especificação dedicada que especifique o comportamento do sistema sob especificação quando a validação é bem-sucedida (
CreateTaskSpec). - Devemos escrever um cenário por papel de usuário.