Criando Nosso Primeiro Page Object
Depois de concluirmos esta aula, nós:
- Saberemos como criar Page Objects que não dependem de configurações ou URLs fixas (hardcoded) de um ambiente específico.
- Saberemos criar Page Objects capazes de interagir com uma página HTML (preencher formulários e navegar).
- Saberemos criar Page Objects capazes de extrair informações de uma página HTML.
1. Passando Configurações Específicas de Ambiente para Nossos Page Objects
Antes de escrevermos nosso primeiro Page Object, precisamos criar duas classes utilitárias que nos ajudam a construir Page Objects totalmente independentes de URLs fixas de ambiente. Podemos criar essas duas classes seguindo estes passos:
Passo 1: Criando a Classe WebDriverEnvironment
Primeiro, criamos um object (ou classe utilitária com métodos estáticos) chamado WebDriverEnvironment. Essa classe fornece o método getBaseUrl(), que nos permite acessar a configuração de URL base passada para nossos testes End-to-End via propriedades de sistema da JVM (System.getProperty("webdriver.base.url")) ou com fallback padrão para http://localhost:8080.
Implementamos o método getBaseUrl() seguindo estes três passos:
- Obter o valor da propriedade de sistema
webdriver.base.url. - Se nenhuma URL base for encontrada, lançar uma
RuntimeExceptionexplicativa. - Retornar a URL base encontrada.
Depois de implementarmos o método getBaseUrl(), o código-fonte de WebDriverEnvironment fica assim:
|
|
Passo 2: Criando a Classe WebDriverUrlBuilder
Segundo, criamos um object chamado WebDriverUrlBuilder. Essa classe fornece um método fábrica estático (buildFromPath) que nos ajuda a substituir URLs fixas (hardcoded) por URLs construídas dinamicamente a partir da URL base do ambiente. Em outras palavras, essa classe permite que nossos testes End-to-End sejam executados em diferentes portas ou ambientes (local, Docker, staging) sem alterar uma única linha dos Page Objects!
Implementamos o método buildFromPath() seguindo estes 7 passos:
- Adicionar o método
buildFromPath(path: String, vararg params: Any?)aoWebDriverUrlBuilder. Esse método recebe o template do caminho (path) e os parâmetros referenciados pelos especificadores de formatação (%d,%s, etc.) presentes na string do caminho, retornando o endereço URL completo. - Se o
pathfor vazio, lançar umaIllegalArgumentException. - Criar o caminho real formatado utilizando
String.format(path, *params). - Obter a URL base invocando
WebDriverEnvironment.getBaseUrl(). - Se a URL base não terminar com o caractere
'/', concatenar'/'ao final da URL base. - Se o caminho formatado começar com o caractere
'/', remover esse primeiro caractere'/'para evitar barras duplas (//). - Concatenar o caminho à URL base e retornar o endereço URL resultante.
Depois de escrevermos o método buildFromPath(), o código-fonte de WebDriverUrlBuilder fica assim:
|
|
Vamos avançar agora e examinar a página de login da nossa aplicação web.
2. Introdução ao Sistema Sob Teste (SUT): A Página de Login
Durante esta aula, criaremos um Page Object que representa a página de login da nossa aplicação web. Essa página atende aos seguintes requisitos:
- A página de login é renderizada quando um usuário envia uma requisição
GETpara o caminho/user/login. - O formulário de login é exibido apenas para usuários anônimos. Se um usuário já autenticado abrir a página de login, ele verá uma mensagem explicando que já está logado (
#authenticated-user-error). - Um usuário pode fazer login informando seu endereço de e-mail (
#email-address) e senha (#password). - Se o login falhar, o usuário é redirecionado de volta para a página de login (
/user/login?error=bad_credentials), que deve exibir uma mensagem de erro (#login-error-alert) explicando que a autenticação falhou.
O código HTML/JSP da página de login (login.jsp / template Thymeleaf equivalente) possui a seguinte estrutura:
|
|
A seguir, construiremos passo a passo o Page Object que representa essa página de login.
3. Criando o Page Object LoginPage
Podemos criar nosso Page Object seguindo estes 6 passos:
Passo 1: Criar a Classe LoginPage e Declarar seus Campos
Primeiro, criamos uma classe pública e final (class em Kotlin já é public e final por padrão) chamada LoginPage e adicionamos dois campos privados a ela:
- O campo
browsercontém a referência para o objetoWebDriverutilizado. - O campo
pageUrlcontém a URL completa da página de login.
|
|
Nota sobre Visibilidade: A classe
LoginPagedeve serpublicporque será utilizada por outras classes de teste End-to-End (como os testes de tarefas) que não estão no mesmo pacotecom.testwithspring.intermediate.web.loginpara autenticar o usuário antes dos testes! Se um Page Object for usado apenas por classes do mesmo pacote/módulo, você pode restringir a visibilidade de seus métodos auxiliares de inspeção cominternal.
Passo 2: Inicializar a URL do Ambiente no Construtor
Segundo, inicializamos o campo pageUrl invocando WebDriverUrlBuilder.buildFromPath("/user/login"):
|
|
Passo 3: Implementar o Método open()
Terceiro, implementamos um método open() que abre a página de login no navegador e retorna um novo objeto LoginPage:
|
|
Regra de Ouro: Todo Page Object que representa uma página HTML inteira deve ter um método
open()que abre a página em questão diretamente via URL. Como esse método nos permite navegar direto para a página testada sem precisar clicar por toda a aplicação desde a home page, podemos escrever testes End-to-End muito mais rápidos e com menos código de setup!
Passo 4: Implementar o Método isOpen()
Quarto, implementamos um método isOpen() que verifica se a página de login está atualmente aberta no navegador. Esse método retorna true se a URL atual do navegador for igual a pageUrl, e false caso contrário:
|
|
Por que criar
isOpen()em todo Page Object de página inteira? Verificar se uma determinada página foi aberta após uma ação é uma asserção extremamente comum nos testes End-to-End (assertThat(loginPage.isOpen()).isTrue()). Ter o métodoisOpen()no Page Object mantém o objetoWebDrivere as URLs encapsulados dentro do Page Object.
Passo 5: Implementar os Métodos de Consulta de Informações da Página
Quinto, implementamos três métodos que buscam informações no DOM da página de login. Esses métodos são utilizados pelos testes End-to-End que verificam se a página de login foi renderizada corretamente:
getEmailAddress(): Retorna o endereço de e-mail atualmente preenchido no input#email-address.getPassword(): Retorna a senha atualmente preenchida no input#password.isLoginFormVisible(): Retornatruese o formulário#login-formestiver visível no DOM (usando!browser.findElements(By.id("login-form")).isEmpty()) efalsecaso contrário.
|
|
Passo 6: Implementar o Método login() e os Métodos Utilitários de Formulário
Sexto, implementamos um método público login(emailAddress: String, password: String): TaskListPage que autentica um usuário utilizando seu e-mail e senha. Esse método é utilizado para autenticar o usuário antes da execução dos nossos testes End-to-End e assume que o login será bem-sucedido.
Implementamos esse método em três subpassos:
- Escrever dois métodos privados utilitários (
typeEmailAddressetypePassword) que digitam o e-mail e a senha nos campos do formulário de login. - Implementar um método privado utilitário (
submitLoginForm) que submete o formulário#login-form. - Adicionar o método público
login(emailAddress: String, password: String)à classeLoginPage, invocando os três métodos utilitários e retornando um novo objetoTaskListPage(browser).
Depois de implementarmos o método login(), o código-fonte completo da classe LoginPage fica assim:
|
|
Por que extraímos os métodos privados
typeEmailAddress,typePasswordesubmitLoginForm? Porque eles tornam o métodologin()muito mais legível e podem ser reutilizados caso adicionemos um segundo métodologinAndExpectFailure(emailAddress: String, password: String): LoginPagepara testar tentativas de login com credenciais inválidas!
O método login() retorna um novo objeto TaskListPage porque nossa aplicação web redireciona o usuário para a página de listagem de tarefas (/) quando o login é bem-sucedido. A estrutura inicial da classe TaskListPage é a seguinte:
|
|
Resumo
Esta aula nos ensinou quatro pontos essenciais:
- Devemos passar as propriedades de configuração específicas do ambiente (como
webdriver.base.url) para os nossos testes End-to-End por meio de propriedades de sistema (WebDriverEnvironment). - Devemos construir todas as URLs dos Page Objects concatenando os caminhos relativos à URL base do ambiente com
WebDriverUrlBuilder.buildFromPath(...). - Se um Page Object representa uma página HTML inteira, ele deve possuir um método
open()que navega diretamente para a página em questão. - Se um Page Object representa uma página HTML inteira, ele deve possuir um método
isOpen()que verifica se a URL atual do navegador corresponde à URL daquela página.