Você ja ouviu falar sobre PageObjects?

Neste artigo, pretendo apresentar o básico sobre PageObjects e como este pattern (sim ele e um pattern) pode nos ajudar com nossos testes de interface no selenium.

O problema

Sempre que criamos testes de interface, por mais organizado que sejamos, com o passar do tempo teremos dificuldades para entender os testes e fazer adaptações/melhorias.
Imagine uma tela com 20 campos, abas, integrações via REST, campos calculados etc.. como seria os testes para esta página?

Imagine isto por todo o código:

driver.FindElement(By.Name("userName")).SendKeys(UserName);
driver.FindElement(By.Name("password")).SendKeys(Password);
driver.FindElement(By.Name("login")).Click();

O problema deste tipo de codigo é a poluição, `driver`, `FindElement`, `By.Name`, `SendKeys` não dizem nada para o nosso negócio!

O que é PageObjects

PageObjects é um pattern que define uma classe para cada página de nossa aplicação, desta forma podemos abstrair detalhes de implementação facilitando os testes.
A ideia e simples, em uma página de Logon temos os campos, Login e Senha, neste caso teremos uma class `LoginPage` com as propriedades `Login` e `Senha` representando cada campo e um método chamado `Logar` representando a ação de logar. Assim teremos as funcionalidades da nossa pagina abstraidas em uma classe.

Exemplo prático

Vamos testar uma tela simples de logon, com dois inputs `login` e `senha` e um botão de login.

Specs

Temos de testar as 3 situações, usuário não digitou nenhum dado, usuário digitou dados incorretos ou usuário digitou dados corretos. Todas descritas na especificação abaixo:

Feature: Logon

Scenario Outline: Ao clicar em logon sem informar usuário e senha o sistema deve exibir uma mensagem
Given Eu acessei o site com o navegador
When Eu pressionar Logon
Then Deve ser exibido a mensagem Campo usuário é obrigatório
And Deve ser exibido a mensagem Campo senha é obrigatório

Scenario Outline: Ao clicar em logon com usuário e senha inválidos
Given Eu acessei o site com o navegador
And Eu inseri os dados de acesso UsuarioFake e SenhaFake
When Eu pressionar Logon
Then Deve ser exibido a mensagem usuário e/ou senha inválido(s)

Scenario Outline: Ao clicar em logon
Given Eu acessei o site com o navegador
And Eu inseri os dados de acesso UsuarioOk e SenhaOk
When Eu pressionar Logon
Then Deve redirecionar para a página inicial

Steps

Para exemplificar, optei por mostrar os steps primeiro para demonstrar como o código fica mais limpo e fácil de ler usando PageObjects:

Repare que a ação de clicar em um botão no site é um método em nossa classe, facilitando bastante a leitura e o entendimento dos nossos testes.

[Given(@"Eu acessei o site com o navegador (.*)")] public void GivenEuAsseseiOSistemaComONavegador(string browser)
{
CurrentBrowser = browser;
CurrentPageObject = new LogonPage(WebBrowser.Current);
}

[Given(@"Eu inseri os dados de acesso (.*) e (.*)")] public void GivenEuInseriOsDadosDeAcesso(string usuario, string senha)
{
CurrentPageObject.Usuario = usuario;
CurrentPageObject.Senha = senha;
}

[When(@"Eu pressionar Logon")] public void WhenEuPressionarLogon()
{
CurrentPageObject.Logon();
}

[Then(@"Deve ser exibido a mensagem (.*)")] public void ThenDeveSerExibidoAMensagem(string mensagem)
{
CurrentPageObject.Alerts.Any(p => p.Mensagem == mensagem).BeTrue();
}

[Then(@"Deve redirecionar para a página inicial")] public void ThenDeveRedirecionarParaAPaginaInicial()
{
return WebBrowser.Current.Title.Contains(PageTitle);
}

PageObjects

Aqui teremos nossa classe com todos os detalhes de implementação da página:
Optei por exibir apenas o que é importante com o objetivo de melhorar a didática deste artigo.

public class LogonPage
{
private IWebDriver driver;

[FindsBy(How = How.Name, Using = "usuario")] private IWebElement usuarioElement;

[FindsBy(How = How.Name, Using = "senha")] private IWebElement senhaElement;

[FindsBy(How = How.Name, Using = "logon")] private IWebElement logorElement;

public string Usuario { get { return usuarioElement.Text; } set { usuarioElement.SendKeys(value); } }
public string Senha { get { return senhaElement.Text; } set { senhaElement.SendKeys(value); } }

public LogonPage(IWebDriver drive)
{
PageFactory.InitElements(driver, this);
}

public void Logon()
{
logorElement.click()
}
}

Conclusão

Programar é a arte de simplificar as coisas, não é o que você consegue fazer com uma tecnologia foda, é como você usa esta tecnologia para simplificar ainda mais o seu código.

PageObjects tem um poder incrível, com este pattern podemos melhorar e muito a manutenibilidade de nossos testes. Isto nos deixa mais animados a escrever ainda mais testes e consequentemente melhorando a cobertura de testes em nosso código.

Pense por exemplo em quando você tem de adicionar um novo campo no html, e esta alteração quebra nossos testes. Com PageObjects, resolver estes tipos de problemas fica bem mais simples e pontual.

Espero ter ajudado!

Reference

* Implementing the Page Object Pattern in C# using Selenium WebDriver
* Behavioural testing in .Net with SpecFlow and Selenium (Part 2)
* PageObjects
* Page Object Pattern
* Selenium PageObjects and PageFactory