= Padrão de Desenvolvimento = [[PageOutline(1-3, Conteúdo)]] == O Problema Original == Os testes automatizados que usam o Selenium 2 interagem diretamente com o objeto webdriver para comandar o navegador de forma a executar os testes programados. Para um conjunto de testes simples é razoável a utilização direta desse objeto sem aplicar nenhum padrão. Porém, quando temos que lidar com pequenos detalhes da página sendo testada, os testes vão ficando cada vez mais complicados. Alguns desses problemas: - Localizadores de elementos da página (xpath, css, ids, etc) são repetidos ao longo dos testes e tem que ser mudados em vários lugares quando a estrutura da página muda. - Múltiplos comandos do Selenium representando uma única ação lógica podem ser repetidos em vários testes. Classes utilitárias que agrupam esses comandos juntos podem facilmente ficar fora de controle e levar ao uso do anti-pattern [http://en.wikipedia.org/wiki/God_object/ God Objects] == A Solução == Essa padrão de projeto deve ser sempre implementado para os testes automatizados. Através dele, qualquer mudança na página sendo testada, não é necessário mudar o teste, precisamos seomente atualizar o objeto que representa a página. Um "page object" representa uma página em particular de uma aplicação e define métodos e propriedades que permitem aos testes interagir com a página. O padrão Page Object pode ser visto com um conjunto de "serviços" que a página sendo testada oferece, ocultando assim, os detalhes e mecanismos internos do funcionamento da página. Dessa forma, conseguimos um desacoplamento dos testes escritos com o TestNG das interações puras do Selenium. Mais adiante veremos um exemplo de código que tornará mais claro esse conceito. == Vantagens == 1. Detalhes da página podem ser refatorados mais facilmente já que as mudanças só precisam ser refletidas no "Page Object" e não nos testes, aumentanto a manutebilidade 2. Interações complexas podem ser modeladas em métodos no page object, e podem ser reutilizados em múltiplos testes 3. As ações de navegação podem ser modeladas como métodos dos page objects que retoram instâncias de outros page objects representando o histórico de navegação do usuário na aplicação 4. Page objects são muito fáceis de escrever, podem ser expandidos quando necessário e aumentam muito a simplicidade e legibilidade dos testes. 5. Torna o teste mais robusto == O Padrão == === Estrutura de Pacotes === Os pacotes seguem o seguinte padrão: - Pacote principal {{{ org.expressolivre.cte }}} - Os casos de teste devem estar contidos em subpacotes que devem corresponder a um módulo do Expresso, por exemplo, módulo de email, agenda: {{{ org.expressolivre.cte.calendar org.expressolivre.cte.email ... }}} - As suítes de testes devem ser subpactoes dos respectivos módulos contendo casos de teste: {{{ org.expressolivre.cte.email.compor org.expressolivre.cte.email.ler org.expressolivre.cte.email.listar org.expressolivre.cte.email.pastas ... }}} - Casos de teste comuns a todos os módulos devem ser colocados no pacote common: {{{ org.expressolivre.cte.common }}} - Da mesma forma que os casos de teste, os page objects devem ficar nos pacotes correspondentes aos módulos: {{{ org.expressolivre.cte.pages.calendar org.expressolivre.cte.pages.email ... }}} - Páginas comuns a todos os módulos devem ser colocadas no pacote common: {{{ org.expressolivre.cte.pages.common }}} === Documentação do Código === As classes dos casos de teste devem ter o seguinte padrão de documentação: {{{ /** * Suite: * * Caso de Teste: * * Link: * * * */ public class EnviarEmailTestCase extends BaseEmailTestCase }}} - Exemplo: {{{ /** * Suite:[FUN02.1] Compor Mensagem * * Caso de Teste: EL-523:Enviar mensagem * * Link: http://testlink.expressolivre.org/linkto.php?tprojectPrefix=EL&item= * testcase&id=EL-523 * * @author L.F.Estivalet (Serpro) * * Created on Jan 4, 2011 at 3:53:04 PM * */ public class EnviarEmailTestCase extends BaseEmailTestCase }}} == Exemplos == A página de envio de email do Expresso irá ser representada através de um !PageObject. Todos os campos que o usuário interage devem ser declarados na classe. [[Image(pageobject.png)]] O código correspondente a página de envio de email pode ser visto abaixo. O ideal é que todos esses elementos tenham um "id" para facilitar a identificação pelo !WebDriver, além de tornar o código mais rápido. Veja que no caso do elemento para criar um novo email e do flag de importante, não existe um "id", então foi necessário usar uma expressão xpath. {{{ package org.expressolivre.cte.pages.email; public class MailPage extends Page { @FindBy(xpath = "//table[@id='folders_tbl']/tbody/tr[1]/td/table/tbody/tr[2]/td/div/span") private WebElement newEmail; @FindBy(id = "to_1") private WebElement to; @FindBy(id = "subject_1") private WebElement subject; @FindBy(id = "send_button_1") private WebElement send; @FindBy(id = "important_message_1") /** Flag de importante no momento de compor um email. */ private WebElement important; @FindBy(xpath = "//div[@id='exmail_main_body']/div[2]/table/tbody/tr[1]/td/table/tbody/tr[1]/td[2]/span[3]") /** Flag de importante de um email aberto. */ private WebElement importantFlag; ... }}} Aqui temos um pedaço do diagrama de classes dos !PageObjects. Note que a classe '''''Page''''' contém métodos que são comuns a todas as classes filhas. Nela, colocamos os métodos básicos para localizar elementos na página, verificar se elementos estão visíveis e aguardar até que um determinado elemento apareça na tela, entre outros. [[Image(diag1.png)]] Abaixo segue uma parte do diagrama de classes dos casos de teste automatizados. Temos uma classe '''''!BaseTestCase''''' contendo as operações básicos de entrar e sair do sistema e outra classe '''''!BaseEmailTestCase''''' com métodos comuns para os casos de teste do módulo email. Todos os testes relativos a esse módulo devem extender essa classe. Da mesma forma temos classes base e especializadas para cada módulo. [[Image(diag2.png)]] ---- ''Última atualização: 25-Jul-2011''