Testando com Selenium / WebDriver e o Padrão de Objeto de Página
1. Introdução
Neste artigo, vamos construir sobreprevious writeupe continuar a melhorar nossos testes Selenium / WebDriver, introduzindo o padrão de objeto de página.
2. Adicionando Selênio
Vamos adicionar uma nova dependência ao nosso projeto para escrever asserções mais simples e legíveis:
org.hamcrest
hamcrest-all
1.3
A versão mais recente pode ser encontrada emMaven Central Repository.
2.1. Métodos adicionais
Na primeira parte da série, usamos alguns métodos de utilitário adicionais que usaremos aqui também.
Começaremos com o métodonavigateTo(String url) - que nos ajudará a navegar por diferentes páginas do aplicativo:
public void navigateTo(String url) {
driver.navigate().to(url);
}
Então, oclickElement(WebElement element) - como o nome indica - se encarregará de executar a ação de clique em um elemento especificado:
public void clickElement(WebElement element) {
element.click();
}
3. Padrão de objeto de página
Selenium nos dá muitas APIs poderosas de baixo nível que podemos usar para interagir com a página HTML.
No entanto, à medida que a complexidade de nossos testes aumenta, a interação com os elementos brutos de baixo nível do DOM não é ideal. Nosso código será mais difícil de alterar, poderá ser interrompido após pequenas alterações na interface do usuário e, simplesmente, será menos flexível.
Em vez disso, podemos utilizar o encapsulamento simples e mover todos esses detalhes de baixo nível para um objeto de página.
Antes de começarmos a escrever nosso objeto de primeira página, é bom ter um entendimento claro do padrão - já que deve nos permitir emular a interação de um usuário com nosso aplicativo.
O objeto de página se comportará como um tipo de interface, que encapsulará os detalhes de nossas páginas ou elementos e exporá uma API de alto nível para interagir com esse elemento ou página.
Como tal, um detalhe importante é fornecer nomes descritivos para nossos métodos (por exemplo, clickButton(), navigateTo()), pois seria mais fácil para nós replicar uma ação realizada pelo usuário e geralmente levará a uma API melhor quando estivermos encadeando as etapas.
Ok, então agora, vamos em frente ecreate our page object - neste caso, nossa página inicial:
public class exampleHomePage {
private SeleniumConfig config;
@FindBy(css=".header--menu > a")
private WebElement title;
@FindBy(css = ".menu-start-here > a")
private WebElement startHere;
// ...
public StartHerePage clickOnStartHere() {
config.clickElement(startHere);
StartHerePage startHerePage = new StartHerePage(config);
PageFactory.initElements(config.getDriver(), startHerePage);
return startHerePage;
}
}
Observe como nossa implementação está lidando com os detalhes de baixo nível do DOM e expondo uma API agradável e de alto nível.
Por exemplo, a anotação@FindBy, nos permite preencher previamente nossoWebElements, isso também pode ser representado usando a APIBy:
private WebElement title = By.cssSelector(".header--menu > a");
Obviamente, ambos são válidos, no entanto, usar anotações é um pouco mais limpo.
Além disso, observe o encadeamento - nosso métodoclickOnStartHere() retorna um objetoStartHerePage - onde podemos continuar a interação:
public class StartHerePage {
// Includes a SeleniumConfig attribute
@FindBy(css = ".page-title")
private WebElement title;
// constructor
public String getPageTitle() {
return title.getText();
}
}
Vamos escrever um teste rápido, em que simplesmente navegamos até a página e verificamos um dos elementos:
@Test
public void givenHomePage_whenNavigate_thenShouldBeInStartHere() {
homePage.navigate();
StartHerePage startHerePage = homePage.clickOnStartHere();
assertThat(startHerePage.getPageTitle(), is("Start Here"));
}
É importante levar em consideração que nossa homepage tem a responsabilidade de:
-
Com base na configuração do navegador, navegue até a página.
-
Uma vez lá, valide o conteúdo da página (neste caso, o título).
Nosso teste é muito direto; navegamos até a página inicial, executamos o clique no elemento "Start Here", que nos levará à página com o mesmo nome e, finalmente, apenas validamos que o título está presente.
Após a execução de nossos testes, o métodoclose() será executado e nosso navegador deve ser fechado automaticamente.
3.1. Separando Preocupações
Outra possibilidade que podemos levar em consideração é separar as preocupações (ainda mais), por ter duas classes distintas, uma cuidará de ter todos os atributos(WebElement ouBy) da nossa página:
public class exampleAboutPage {
@FindBy(css = ".page-header > h1")
public static WebElement title;
}
O outro cuidará de ter toda a implementação da funcionalidade que queremos testar:
public class exampleAbout {
private SeleniumConfig config;
public exampleAbout(SeleniumConfig config) {
this.config = config;
PageFactory.initElements(config.getDriver(), exampleAboutPage.class);
}
// navigate and getTitle methods
}
Se estivermos usando atributos comoBye não usarmos o recurso de anotação, é recomendado adicionar um construtor privado em nossa classe de página para evitar que seja instanciado.
É importante mencionar que precisamos passar a classe que contém as anotações, neste caso, a classeexampleAboutPage, em contraste com o que fizemos em nosso exemplo anterior, passando a palavra-chavethis.
@Test
public void givenAboutPage_whenNavigate_thenTitleMatch() {
about.navigateTo();
assertThat(about.getPageTitle(), is("About example"));
}
Observe como agora podemos manter todos os detalhes internos da interação com nossa página na implementação e, aqui, podemos realmente usar esse cliente em um nível alto e legível.
4. Conclusão
Neste tutorial rápido, focamos em melhorar nosso uso deSelenium/WebDriver with the help of the Page-Object Pattern. Passamos por diferentes exemplos e implementações, para ver as maneiras práticas de utilizar o padrão para interagir com nosso site.
Como sempre, a implementação de todos esses exemplos e snippets pode ser encontradaover on GitHub. Este é um projeto baseado em Maven, portanto deve ser fácil importar e executar.