Automação no Selenium: Modelo de Objeto de Página e Fábrica de Páginas

Publicados: 2022-03-11

Escrever testes automatizados é mais do que apenas um luxo para qualquer equipe de desenvolvimento de software ágil. É uma necessidade e uma ferramenta essencial para encontrar bugs rapidamente durante as fases iniciais dos ciclos de desenvolvimento de software. Quando há um novo recurso que ainda está em fase de desenvolvimento, os desenvolvedores podem executar testes automatizados e ver como outras partes do sistema são afetadas por essas alterações. Este artigo explicará como você pode acelerar os testes automatizados usando o modelo Page Object no Selenium.

Por meio da automação de testes, é possível reduzir o custo de correção de bugs e trazer melhorias gerais para o processo de garantia de qualidade de software (QA). Com testes adequados, os desenvolvedores têm a chance de encontrar e resolver bugs antes mesmo de chegar ao controle de qualidade. A automação de teste nos ajuda ainda a automatizar casos de teste e recursos que estão constantemente regredindo. Dessa forma, os QAs têm mais tempo para testar outras partes do aplicativo. Além disso, isso ajuda a garantir a qualidade do produto nos lançamentos de produção. Como resultado, obtemos produtos efetivamente mais estáveis ​​e um processo de controle de qualidade mais eficiente.

A automação do Selenium simplifica a automação de testes para aplicativos da web

Selenium simplifica a automação de testes para aplicativos da web
Tweet

Embora escrever testes automatizados possa parecer uma tarefa fácil para desenvolvedores e engenheiros, ainda existe a possibilidade de acabar com testes mal implementados, além do alto custo de manutenção de código em qualquer processo ágil. Tentar entregar constantemente mudanças ou recursos em qualquer projeto de desenvolvimento ágil pode custar caro quando os testes estão envolvidos. Alterar um elemento em uma página da Web que 20 testes dependem exigirá que um passe por essas 20 rotinas de teste e atualize cada uma para se adaptar a essa mudança recém-introduzida. Isso não apenas pode consumir muito tempo, mas também um sério fator de desmotivação quando se trata de implementar testes automatizados desde o início.

Mas, e se pudéssemos fazer a alteração em apenas um lugar e fazer com que todas as rotinas de teste relevantes a usassem? Neste artigo, veremos os testes automatizados no Selenium e como podemos usar os modelos Page Object para escrever rotinas de teste que podem ser mantidas e reutilizáveis.

Modelo de objeto de página no Selenium

O modelo de objeto de página é um padrão de design de objeto no Selenium, onde as páginas da Web são representadas como classes e os vários elementos na página são definidos como variáveis ​​na classe. Todas as possíveis interações do usuário podem ser implementadas como métodos na classe:

 clickLoginButton(); setCredentials(user_name,user_password);

Como métodos bem nomeados em classes são fáceis de ler, isso funciona como uma maneira elegante de implementar rotinas de teste que são legíveis e fáceis de manter ou atualizar no futuro. Por exemplo:

Para dar suporte ao modelo Page Object, usamos o Page Factory. O Page Factory no Selenium é uma extensão do Page Object e pode ser usado de várias maneiras. Neste caso, usaremos o Page Factory para inicializar os elementos da web que são definidos em classes de página da web ou objetos de página.

As classes de página da Web ou Objetos de página contendo elementos da Web precisam ser inicializados usando o Page Factory antes que as variáveis ​​do elemento da Web possam ser usadas. Isso pode ser feito simplesmente através do uso da função initElements em PageFactory:

 LoginPage page = new LoginPage(driver); PageFactory.initElements(driver, page);

Ou, ainda mais simples:

 LoginPage page = PageFactory.intElements(driver,LoginPage.class)

Ou, dentro do construtor da classe da página da web:

 public LoginPage(WebDriver driver) { this.driver = driver; PageFactory.initElements(driver, this); }

O Page Factory inicializará cada variável WebElement com uma referência a um elemento correspondente na página da Web real com base nos “localizadores” configurados. Isso é feito através do uso de anotações @FindBy . Com esta anotação, podemos definir uma estratégia de busca do elemento, juntamente com as informações necessárias para identificá-lo:

 @FindBy(how=How.NAME, using="username") private WebElement user_name;

Toda vez que um método é chamado nessa variável WebElement , o driver primeiro o encontrará na página atual e, em seguida, simulará a interação. Caso estejamos trabalhando com uma página simples, sabemos que encontraremos o elemento na página toda vez que a procurarmos, e também sabemos que eventualmente sairemos dessa página e não retornaremos a ela, podemos armazenar em cache o campo pesquisado usando outra anotação simples:

 @FindBy(how=How.NAME, using="username") @CacheLookup private WebElement user_name;

Toda essa definição da variável WebElement pode ser substituída por sua forma muito mais concisa:

 @FindBy(name="username") private WebElement user_name;

A anotação @FindBy suporta várias outras estratégias que tornam as coisas um pouco mais fáceis:

 id, name, className, css, tagName, linkText, partialLinkText, xpath
 @FindBy() private WebElement user_name; @FindBy(name="passsword") private WebElement user_password; @FindBy(className="h3") private WebElement label; @FindBy(css=”#content”) private WebElement text;

Uma vez inicializadas, essas variáveis ​​WebElement podem ser usadas para interagir com os elementos correspondentes na página. O código a seguir irá, por exemplo:

 user_password.sendKeys(password);

… envie a sequência de pressionamentos de tecla fornecida para o campo de senha na página e é equivalente a:

 driver.findElement(By.name(“user_password”)).sendKeys(password);

Seguindo em frente, muitas vezes você encontrará situações em que precisa encontrar uma lista de elementos em uma página, e é aí que o @FindBys é útil:

 @FindBys(@FindBy(css=”div[class='yt-lockup-tile yt-lockup-video']”))) private List<WebElement> videoElements;

O código acima encontrará todos os elementos div com dois nomes de classe “yt-lockup-tile” e “yt-lockup-video”. Podemos simplificar ainda mais substituindo-o pelo seguinte:

 @FindBy(how=How.CSS,using="div[class='yt-lockup-tile yt-lockup-video']") private List<WebElement> videoElements;

Além disso, você pode usar @FindAll com várias anotações @FindBy para procurar elementos que correspondam a qualquer um dos localizadores fornecidos:

 @FindAll({@FindBy(how=How.ID, using=”username”), @FindBy(className=”username-field”)}) private WebElement user_name;

Agora que podemos representar páginas da Web como classes Java e usar o Page Factory para inicializar variáveis ​​WebElement facilmente, é hora de vermos como podemos escrever testes Selenium simples usando o padrão Page Object e o Page Factory.

Projeto simples de automação de teste de selênio em Java

Para nosso tutorial de modelo de objeto de página, vamos automatizar a inscrição do desenvolvedor no Toptal. Para isso, precisamos automatizar as seguintes etapas:

  • Acesse www.toptal.com

  • Clique no botão “Aplicar como desenvolvedor”

  • Na página do portal, verifique primeiro se está aberto

  • Clique no botão “Participar do Toptal”

  • Preencha o formulário

  • Envie o formulário clicando no botão “Join Toptal”

Configurando um projeto

  • Baixe e instale o Java JDK

  • Baixe e instale o InteliJ Idea

  • Criar um novo projeto Maven

  • Vincule o “Project SDK” ao seu JDK, por exemplo: no Windows “C:\Program Files\Java\jdkxxx”

  • Configure groupId e artefatoId:

 <groupId>SeleniumTEST</groupId> <artifactId>Test</artifactId>
  • Adicione dependências Selenium e JUnit Maven no arquivo POM do seu projeto
 <dependencies> <!-- JUnit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <!-- Selenium --> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-firefox-driver</artifactId> <version>${selenium.version}</version> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-support</artifactId> <version>${selenium.version}</version> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>${selenium.version}</version> </dependency> </dependencies>

Substitua a versão do Selenium e a versão do JUnit pelos números de versão mais recentes que podem ser encontrados pesquisando o JUnit Maven no Google e no site do Selenium.

Neste ponto, se a compilação automática estiver habilitada, as dependências devem começar a ser baixadas automaticamente. Caso contrário, basta ativar Plugins > instalar > instalar: instalar no painel Projetos Maven no lado direito do seu IDE IntelliJ Idea.

captura de tela do IDE do tutorial de teste de selênio

Uma vez que o projeto tenha sido inicializado, podemos começar a criar nosso pacote de teste em “src/test/java”. Nomeie o pacote “com.toptal” e crie mais dois pacotes abaixo dele: “com.toptal.webpages” e “com.toptal.tests”.

captura de tela do tutorial de teste de selênio

Manteremos nossas classes Page Object/Page Factory em “com.toptal.webpages” e as rotinas de teste em “com.toptal.tests”.

Agora, podemos começar a criar nossas classes Page Object.

Objeto de página inicial

O primeiro que precisamos implementar é para a homepage da Toptal (www.toptal.com). Crie uma classe em “com.toptal.webpages” e nomeie-a como “HomePage”.

 package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.How; import org.openqa.selenium.support.PageFactory; public class HomePage { private WebDriver driver; //Page URL private static String PAGE_URL="https://www.toptal.com"; //Locators //Apply as Developer Button @FindBy(how = How.LINK_TEXT, using = "APPLY AS A DEVELOPER") private WebElement developerApplyButton; //Constructor public HomePage(WebDriver driver){ this.driver=driver; driver.get(PAGE_URL); //Initialise Elements PageFactory.initElements(driver, this); } public void clickOnDeveloperApplyButton(){ developerApplyButton.click(); } }

Determinando localizadores de elementos

Na homepage da Toptal estamos interessados ​​em um elemento em particular, que é o botão “Apply as a Developer”. Podemos encontrar esse elemento combinando o texto, que é o que estamos fazendo acima. Ao modelar páginas da Web como classes de Objetos de Página, encontrar e identificar elementos muitas vezes pode se tornar uma tarefa árdua. Com as ferramentas de depuração do Google Chrome ou Firefox, isso pode ser facilitado. Ao clicar com o botão direito em qualquer elemento de uma página, você pode ativar a opção “Inspecionar Elemento” no menu de contexto para obter informações detalhadas sobre o elemento.

Uma maneira comum (e minha preferida) é encontrar elementos usando a extensão FireBug do Firefox, em combinação com o driver da Web do Firefox no Selenium. Após instalar e habilitar a extensão FireBug, você pode clicar com o botão direito na página e selecionar “Inspecionar elemento com FireBug” para abrir o FireBug. Na guia HTML do FireBug, você pode copiar o XPath, CSS Path, Tag name ou “Id” (se disponível) de qualquer elemento da página.

modelo de objeto de página em selênio: determinando localizadores de elementos

Ao copiar o XPath do elemento na captura de tela acima, podemos criar um campo WebElement para ele em nosso objeto de página da seguinte maneira:

 @FindBy(xpath = "/html/body/div[1]/div/div/header/div/h1") WebElement heading;

Ou para simplificar, podemos usar o nome da tag “h1” aqui, desde que identifique exclusivamente o elemento em que estamos interessados:

 @FindBy(tagName = "h1") WebElement heading;

Objeto de página DeveloperPortalPage

Em seguida, precisamos de um objeto de página que represente a página do portal do desenvolvedor, que podemos acessar clicando no botão “Aplicar como desenvolvedor”.

Nesta página, temos dois elementos de interesse. Para determinar se a página foi carregada, queremos verificar a existência do título. E também queremos um campo WebElement para o botão “Join Toptal”.

 package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class DeveloperPortalPage { private WebDriver driver; @FindBy(xpath = "/html/body/div[1]/div/div/header/div/h1") private WebElement heading; @FindBy(linkText = "JOIN TOPTAL") private WebElement joinToptalButton; //Constructor public DeveloperPortalPage (WebDriver driver){ this.driver=driver; //Initialise Elements PageFactory.initElements(driver, this); } //We will use this boolean for assertion. To check if page is opened public boolean isPageOpened(){ return heading.getText().toString().contains("Developer portal"); } public void clikOnJoin(){ joinToptalButton.click(); } }

Objeto de página DeveloperApplyPage

E, finalmente, para nosso terceiro e último objeto de página para este projeto, definimos um que representa a página que contém o formulário de aplicativo do desenvolvedor. Como temos que lidar com vários campos de formulário aqui, definimos uma variável WebElement para cada campo de formulário. Encontramos cada campo por seu “id” e definimos métodos setter especiais para cada campo que simulam pressionamentos de tecla para os campos correspondentes.

 package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class DeveloperApplyPage { private WebDriver driver; @FindBy(tagName = "h1") WebElement heading; @FindBy() WebElement developer_email; @FindBy() WebElement developer_password; @FindBy() WebElement developer_password_confirmation; @FindBy() WebElement developer_full_name; @FindBy() WebElement developer_skype; @FindBy() WebElement join_toptal_button; //Constructor public DeveloperApplyPage(WebDriver driver){ this.driver=driver; //Initialise Elements PageFactory.initElements(driver, this); } public void setDeveloper_email(String email){ developer_email.clear(); developer_email.sendKeys(email); } public void setDeveloper_password(String password){ developer_password.clear(); developer_password.sendKeys(password); } public void setDeveloper_password_confirmation(String password_confirmation){ developer_password_confirmation.clear(); developer_password_confirmation.sendKeys(password_confirmation); } public void setDeveloper_full_name (String fullname){ developer_full_name.clear(); developer_full_name.sendKeys(fullname); } public void setDeveloper_skype (String skype){ developer_skype.clear(); developer_skype.sendKeys(skype); } public void clickOnJoin(){ join_toptal_button.click(); } public boolean isPageOpened(){ //Assertion return heading.getText().toString().contains("Apply to join our network as a developer"); } }

Escrevendo um teste simples de selênio

Com as classes Page Object representando nossas páginas e as interações do usuário como seus métodos, agora podemos escrever nossa rotina de teste simples como uma série de chamadas e asserções de métodos simples.

 package com.toptal.tests; import com.toptal.webpages.DeveloperApplyPage; import com.toptal.webpages.DeveloperPortalPage; import com.toptal.webpages.HomePage; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; import java.net.URL; import java.util.concurrent.TimeUnit; public class ApplyAsDeveloperTest { WebDriver driver; @Before public void setup(){ //use FF Driver driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); } @Test public void applyAsDeveloper() { //Create object of HomePage Class HomePage home = new HomePage(driver); home.clickOnDeveloperApplyButton(); //Create object of DeveloperPortalPage DeveloperPortalPage devportal= new DeveloperPortalPage(driver); //Check if page is opened Assert.assertTrue(devportal.isPageOpened()); //Click on Join Toptal devportal.clikOnJoin(); //Create object of DeveloperApplyPage DeveloperApplyPage applyPage =new DeveloperApplyPage(driver); //Check if page is opened Assert.assertTrue(applyPage.isPageOpened()); //Fill up data applyPage.setDeveloper_email("[email protected]"); applyPage.setDeveloper_full_name("Dejan Zivanovic Automated Test"); applyPage.setDeveloper_password("password123"); applyPage.setDeveloper_password_confirmation("password123"); applyPage.setDeveloper_skype("automated_test_skype"); //Click on join //applyPage.clickOnJoin(); } @After public void close(){ driver.close(); } }

Executando o teste

Neste ponto, a estrutura do seu projeto deve ficar assim:

exemplo de teste de selênio

Se você deseja executar o teste, selecione “ApplyAsDeveloperTest” na árvore, clique com o botão direito nele e selecione Executar 'ApplyAsDeveloperTest' .

exemplo de teste de selênio

Depois que o teste for executado, você poderá ver os resultados no canto inferior esquerdo do seu IDE:

exemplo de teste de selênio

Conclusão

O Page Object e o Page Factory facilitam a modelagem de páginas da Web no Selenium e os testam automaticamente, tornando a vida dos desenvolvedores e do controle de qualidade muito mais simples. Quando bem feitas, essas classes Page Object podem ser reutilizadas em todo o seu conjunto de testes e dar a você a oportunidade de implementar testes automatizados do Selenium para seus projetos desde o início, sem comprometer o desenvolvimento ágil. Ao abstrair as interações do usuário em seus modelos de objeto de página e manter suas rotinas de teste leves e simples, você pode adaptar seu conjunto de testes às mudanças de requisitos com pouco esforço.

Espero ter conseguido mostrar a você como escrever um código de teste agradável e limpo que seja fácil de manter. Vou terminar o artigo com minha citação favorita de controle de qualidade:

Pense duas vezes, codifique uma vez!

Relacionado: Web Scraping com um navegador sem cabeça: um tutorial de marionetista