Já faz um tempo que queria falar sobre esse assunto mas estava esperando para o blog ter mais conteúdo e mais acesso.
Quem desenvolve testes com o Selenium já notou que a IDE é só para vender o peixe. É lindo acreditar que criar teste automatizado é só ligar o botão de gravação, sair navegando pelas páginas e salvar os arquivos para uma próxima execução. Tudo parece perfeito até o momento em que você precisa dar manutenção nos seus testes.
O padrão de PageObjects ajuda em:
- Manutençao do código
- Clareza no código
- Reuso de funcionalidades
- Organização dos testes
A ideia do padrão é criar classes para cada tela do seu sistema para que ela contenha as funcionalidades da própria e encapsule a lógica dos comandos do Selenium.
Vamos para um exemplo prático.
Criei uma página com a funcionalidade de uma busca.
Vamos criar uma classe representando a tela de busca e suas funcionalidades:
package br.com.seuenium; import com.thoughtworks.selenium.DefaultSelenium; public class TelaBusca { private DefaultSelenium selenium; public TelaBusca(DefaultSelenium selenium) { this.selenium = selenium; } public void abrir() { selenium.open("/testes/8-pageobjects/buscar.php"); } public void buscar(String termo) { selenium.type("name=q", termo); selenium.click("//input[@type='submit']"); selenium.waitForPageToLoad("10000"); } }
O construtor da classe recebe o objeto DefaultSelenium para poder acessar os comandos da api. Os dois métodos criados representam as funcionalidades da tela: o primeiro que é abrir a própria página e o segundo que é buscar por um determinado termo. Note que a lógica do Selenium de digitar o termo no campo e em seguida clicar no botão de submit foi encapsulada pelo método buscar.
Agora vamos criar o nosso caso de teste. A ideia do teste é buscar pelo termo “Selenium” e depois validar que o termo continua presente na caixa de busca. A tela vai ficar assim:
E o TestCase:
package br.com.seuenium; import org.junit.*; import com.thoughtworks.selenium.DefaultSelenium; public class BuscaTestCase { private DefaultSelenium selenium; @Before public void start() { selenium = new DefaultSelenium("localhost", 4444, "*firefox", "http://www.seuenium.com.br"); selenium.start(); } @Test public void testSucesso() { TelaBusca tela = new TelaBusca(selenium); tela.abrir(); tela.buscar("Selenium"); //validacoes Assert.assertEquals("Selenium", tela.carregarTermoBuscado()); } @After public void stop() { selenium.stop(); } }
Note que o método “testSucesso” instancia a classe TelaBusca, chama o método “abrir” e depois o “buscar”. Em seguida vêm as validações, que por enquanto é só uma. O testCase dá um asserEquals do termo “Selenium” com o método “carregarTermoBuscado” que ainda não foi implementado. Vamos implementá-lo:
package br.com.seuenium; import com.thoughtworks.selenium.DefaultSelenium; public class TelaBusca { private DefaultSelenium selenium; public TelaBusca(DefaultSelenium selenium) { this.selenium = selenium; } public void abrir() { selenium.open("/testes/8-pageobjects/buscar.php"); } public void buscar(String termo) { selenium.type(localizadorDoCampoDoTermo(), termo); selenium.click("//input[@type='submit']"); selenium.waitForPageToLoad("10000"); } public String carregarTermoBuscado() { return selenium.getValue(localizadorDoCampoDoTermo()); } private String localizadorDoCampoDoTermo() { return "name=q"; } }
Note que o método foi implementado e também houve um pequeno refactoring. Tanto o método “carregarTermoBuscado” como o “buscar” compartilhavam um mesmo locator que correspondia ao input text do termo. Foi criado o método “localizadorDoCampoDoTermo” para evitar duplicidade e facilitar a manutenção do teste. Caso o campo mude de nome no futuro, você apenas terá que alterar esse método e não ficará caçando em seus testes quem usa esse campo. A mesma ideia poderia ser aplicada ao locator do botão buscar.
Continuando com as validações, vamos conferir se a tela retornou os 2 resultados esperados.
Vamos adicionar a seguinte validação no TestCase:
List<String> resultadosEsperados = Arrays.asList( "Selenium IDE is a Firefox add-on that records clicks, typing, and other actions to make a test, which you can play back in the browser.", "Selenium is a chemical element with the atomic number 34, represented by the chemical symbol Se, an atomic mass of 78.96."); Assert.assertEquals(resultadosEsperados, tela.carregarResultados());
E na classe da tela vamos implementar o método que retorna a lista de resultados:
public List<String> carregarResultados() { List<String> resultados = new ArrayList<String>(); Integer xpathCount = selenium.getXpathCount(localizadorDosResultados()).intValue(); for (int i = 1; i <= xpathCount; i++) { resultados.add(selenium.getText(localizadorDosResultados()+"["+i+"]")); } return resultados; }
Bom gente, esse exemplo é bem simples, mas já da pra ver os ganhos em usar PageObjects.
Você pode fazer o download aqui de um projeto maven com os testes desse post.
Há outros tópicos sobre PageObjects que pretendo falar em outros posts. Para quem não sabe, vai sair uma versão nova do Selenium, a 2.0, e ela tem suporte nativo para PageObjects. Esperem o próximo post para ler mais sobre o Selenium 2.0. Aguardem!!!