Imagine uma aplicação web com cem testes funcionais automatizados e, ao fazermos uma alteração de funcionalidade desejamos executar os testes de regressão com o intuito de verificar se as outras não foram danificadas. Após a execução sem problemas, podemos verificar que o tempo gasto nestes foi algo em torno de seis horas. Diante disso, precisamos otimizar para reduzir o tempo de execução dos nossos testes funcionais automatizados.

Uma possível solução seria a distribuição da execução destes utilizando o Selenium GRID usando duas máquinas virtualizadas com o apoio do VirtualBox. Com essa distribuição de tarefas, o que levaria seis horas para rodar tudo passaria a gastar metade do tempo. Este artigo destaca essa solução na prática, mas com menos testes.

O Selenium WebDriver e o TestNG permitem escrever, executá-los e escolher em quais navegadores isso ocorrerá. Veremos nesse artigo como isso funciona na prática. Mas antes de iniciarmos o desenvolvimento, vamos conhecer melhor as ferramentas utilizadas.

Selenium WebDriver

É um framework de automação de testes, fornecendo uma API em diferentes linguagens, como Java, C#, Python e outros, que automatiza ações de usuário na interface web em diferentes navegadores. Ele serve para integrar o código-fonte com a tela do sistema e com o banco de dados. Sua arquitetura é apresentada na Figura 1.

Arquitetura do Selenium WebDriver
Figura 1. Arquitetura do Selenium WebDriver

Selenium Grid

Este permite distribuir os testes escritos com a API WebDriver em várias máquinas físicas ou virtuais. Além disso, o Selenium Grid tem dois conceitos importantes para o seu funcionamento:

  • Hub: atua como um ponto central, ou seja, um servidor que carregará todos os testes a serem executados e distribuídos para o(s) Node(s). Recomenda-se criar apenas um único Hub para comandar;
  • Node: atua como um ponto ligado ao Hub central esperando receber os testes a serem executados. Vários nodes podem ser ligados ao Hub central, mas nenhum pode se ligar a outro.

Vejamos na Figura 2 a arquitetura do Selenium Grid.

Arquitetura do Selenium Grid
Figura 2. Arquitetura do Selenium Grid

Para apoiar a qualidade do software, a família Selenium ainda conta com um IDE próprio para ajudar na execução de testes funcionais

Para esse artigo também usaremos, em conjunto com o IDE Eclipse :

  • VirtualBox: É um software de virtualização de plataforma, que permite ao usuário instalar e executar sistemas operacionais independentes em um único computador.
  • TestNG: É um framework de criação e execução de testes de unidade em Java;
  • Apache Maven: É uma ferramenta de gestão de projetos de software que utiliza o conceito de POM (modelo de objeto de projeto) para a compilação de projetos que envolve a construção, elaboração de relatórios e documentação em uma central de informações, sendo compatível com diferentes linguagens. Mas usaremos a linguagem Java 8 para esse exemplo.

Visão geral

Desenvolveremos os testes funcionais distribuídos na aplicação web pronta que está em http://artigo.pe.hu.

Então o primeiro teste consiste em acessar na página inicial a opção “Trabalhe Conosco”, preencher todos os campos do formulário e enviá-lo. Ao final, verifica-se se o envio deu certo. O segundo consiste em entrar na página inicial e clicar na opção Conversor -> Temperatura e ao carregar a página devemos testar todas as combinações de cálculos de conversão de temperatura, verificando se todos os resultados estão corretos.

O primeiro teste será executado no Firefox da primeira máquina virtual (Ubuntu; Node 1) e o segundo no Google Chrome da segunda máquina virtual (Ubuntu; Node 2).

Para que tudo funcione seguiremos os passos a seguir:

  1. Configuração das máquinas i. Configuração do Hub central na nossa máquina física local utilizando o Selenium Grid;ii. Virtualização de duas máquinas com Ubuntu utilizando o VirtualBox;iii. Configuração dos Nodes nas duas máquinas virtuais, e após, ligá-las ao Hub central utilizando Selenium Grid;
  2. Criação de um novo projeto no Eclipse com Maven utilizando o Java 8;
  3. Criação das classes utilizando o Selenium WebDriver;
  4. Criação das classes de testes utilizando o TestNG e posterior execução dos mesmos;

Assim, a visão geral desse artigo pode ser vista na Figura 3.

Fluxo de execução da visão geral
Figura 3. Fluxo de execução da visão geral

Configuração das máquinas

Para começar precisamos baixar o Selenium Standalone Server .jar (vide seção Links). Para criar o hub central em nossa máquina física local executaremos o comando a seguir (use o shell ou prompt de comando, pois o comando funciona para todos os sistemas operacionais):

java -jar selenium-server-standalone-2.47.1.jar -role hub

Caso o comando não funcione, provavelmente é porque ainda não tem instalado o Java na máquina ou não configurou bem as varáveis de ambiente.

Se tudo ocorreu bem, a mensagem apresentada será semelhante à Figura 4.

Mensagem de sucesso
Figura 4. Mensagem de sucesso

Repare na linha em destaque que o Selenium Standalone Server nos forneceu o endereço, que servirá para registar os Nodes ao Hub central.

Se colocarmos no navegador o endereço http://192.168.0.6:4444/grid/console, podemos observar quais nodes estão sob controle do Hub central, além das suas configurações, como mostra a Figura 5.

Página web que exibe o(s) Node(s) ligado(s) ao Hub central
Figura 5. Página web que exibe o(s) Node(s) ligado(s) ao Hub central

Precisamos agora virtualizar duas máquinas utilizando o VirtualBox e depois fazer as configurações de Nodes nelas. Baixe ISO do VirtualBox na versão do sistema operacional da máquina local (vide seção Links), que no nosso caso é o Ubuntu em sua versão “14.10” 32 bits.

Com o programa aberto, clique em Novo e informe dentro do VirtualBox o valor “ubuntu 1” no campo nome e selecione a versão “Ubuntu (32 bits)”, como na Figura 6.

VirtualBox e o “Nome e Sistema Operacional” da máquina virtual
Figura 6. VirtualBox e o “Nome e Sistema Operacional” da máquina virtual

Após clicar em próximo, a tela seguinte será para informar o tamanho da memória. Como serão configuradas duas máquinas, selecione apenas ¼ da memória total e clique em “Próximo >”.

Na nova tela configuraremos o HD: certifique-se que as opções a seguir serão marcadas nas próximas telas:

  • opção “Criar um disco rígido virtual agora” e clique em “Criar”;
  • tipo de arquivo de disco rígido de ser o “VDI (VirtualBox Disk Image)”;
  • o tipo “Armazenamento em disco rígido físico” deve ser selecionado;
  • o tamanho deve ser “Tamanho Fixo”;
  • para a tela de “Localização e tamanho do arquivo” deixe o mínimo recomendável, que no caso são 8 GB para o Ubuntu, em seguida clique em “Criar”.

Depois de termos criado a nossa máquina virtual, selecione-a na janela principal do VirtualBox e depois clique no menu “Configurações”. Na janela aberta selecione o item “Rede” no lado esquerdo e depois clique na aba “Adaptador 1” e, logo em seguida, na opção “Conectado a:” escolha o item “Placa em modo Bridge”, como mostra a Figura 7.

Configurações de rede
Figura 7. Configurações de rede

Ainda com a opção “ubuntu 1” selecionada, clique no menu de opções “Iniciar (T)” para iniciar a instalação do Ubuntu. Assim, uma janela como a Figura 8 é apresentada. Selecione o local onde está a ISO ou disco de instalação e depois clique em “Iniciar”.

Seleção do disco rígido de boot
Figura 8. Seleção do disco rígido de boot

Para criar a segunda máquina virtual basta repetir os mesmos passos, porém, use o nome “ubuntu 2”. Após a criação, instale o Java em ambas e os respectivos browsers, de acordo com as instruções passadas no início do artigo.

Para o Chrome, vamos baixar o driver direto do site do Selenium (vide seção Links). Vá até a seção “Third Party Drivers, Bindings, and Plugins → Browser” e clique no link chromedriver_linux32.zip. Este driver é necessário para facilitar a comunicação do navegador com o Selenium Standalone Server (Hub central). Após baixar realize a configuração com os seguintes comandos:

  • No Ubuntu) e Mac OS X:
    export PATH=$PATH:/caminho/para/chromedriver
  • No Windows:
    PATH=$PATH;\caminho\para\chromedriver

Para o Firefox não precismos baixar ou configurar nenhum driver.

Para configurar os nodes nas máquinas virtuais e ligá-las a Hub central precisamos executar um comanda Java em cada máquina virtual. Para o Node 1 execute o seguinte comando:

java -jar selenium-server-standalone-2.47.1.jar -role node -hub 
http://192.168.0.6:4444/grid/register -browser browserName="firefox", 
maxInstances=1,platform=LINUX

E para o Node 2 execute o comando:

java -jar selenium-server-standalone-2.47.1.jar -role node -hub 
http://192.168.0.6:4444/grid/register -browser browserName="chrome", 
maxInstances=1,platform=LINUX

Note que o valor “http://192.168.0.6:4444/grid/register” a frente do comando -hub se refere ao endereço do Hub central, assim fizemos o registro ao Hub central.

Repare também que no comando “-browser” definimos ao atributo -browserName o nome do navegador que estará apto para a executar os testes funcionais da aplicação web.

Ao final podemos visualizar no endereço http://192.168.0.6:4444/grid/console os Nodes que registramos, como mostra a Figura 9.

Selenium Standalone Server (Hub central) exibindo os Nodes registrados
Figura 9. Selenium Standalone Server (Hub central) exibindo os Nodes registrados

Criando o projeto

No Eclipse pressione CTRL+N e selecione o item Project Maven. Ao clicar em "Next >" uma nova janela se abrirá e nela marque o checkbox "Create a simple project" e clique em "Next"novamente. Uma nova tela aparecerá e deve ser preenchida conforme mostra a Figura 10.

Configurações do projeto Maven
Figura 10. Configurações do projeto Maven

Para adicionar as dependências no POM do Maven abra o pom.xml e acrescente o código da Listagem 1 entre as tags . Logo após execute o “Maven Update Project” utilizando o ALT+F5.

<build>
   <plugins>
     <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-compiler-plugin</artifactId>
       <!-- Versão do plugin maven -->
       <version>3.3</version>
       <configuration>
         <!-- Versão do java -->
         <source>1.8</source>
         <target>1.8</target>
      </configuration>
    </plugin>
  </plugins>
</build>
<properties>
     <!-- Versão do selenium -->
     <selenium.version>2.47.1</selenium.version>
</properties>
<dependencies>
     <!-- Selenium -->
     <dependency>
       <groupId>org.seleniumhq.selenium</groupId>
       <artifactId>selenium-java</artifactId>
       <version>${selenium.version}</version>
       <scope>test</scope>
    </dependency>
    <!--Driver do Selenium remoto -->
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-remote-driver</artifactId>
        <version>${selenium.version}</version>
        <scope>test</scope>
    </dependency>
    <!-- TestNG -->
    <dependency>
        <groupId>org.testng</groupId>
           <artifactId>testng</artifactId>
        <version>6.9.6</version>
        <scope>test</scope>
    </dependency>
</dependencies>
Listagem 1. Dependências do projeto

Criando as classes principais

Precisamos agora criar a estrutura de pacotes da Figura 11 em nosso projeto no diretório /projeto-maven/src/main/java.

Estrutura de pacote
Figura 11. Estrutura de pacote

Na Interface EstabelecerDriver.java precisamos adicionar o código da Listagem 2 para definir as propriedades que o WebDriver usará para acessar o browser. Assim, ao solicitar uma nova sessão, o cliente pode especificar o browser e a plataforma a serem usadas.

public interface EstabelecerDriver {
 
   //Declarações de métodos
 
   WebDriver obterObjetoWebDriver(DesiredCapabilities desiredCapabilities);
 
   WebDriver obterObjetoWebDriverRemoto(DesiredCapabilities desiredCapabilities,
    Platform  plataforma, String enderecoRemoto);
 
   DesiredCapabilities obterCapacidadesDesejadas();
}
Listagem 2. Código da Interface EstabelecerDriver.java

Para o Enum TipoDriver adicionaremos o código da Listagem 3. Este servirá para definir tipos de drivers, que contém métodos úteis dos quais iremos utilizar nas classes de testes para a chamada de instâncias de WebDriver's (Firefox e Chrome).

public enum TipoDriver implements EstabelecerDriver {

 FIREFOX {
             
   public DesiredCapabilities obterCapacidadesDesejadas() {
     DesiredCapabilities capabilities = DesiredCapabilities.firefox();
     return capabilities;
   }

 public WebDriver obterObjetoWebDriver(DesiredCapabilities capabilities) {
    return new FirefoxDriver(capabilities);
 }

 public WebDriver obterObjetoWebDriverRemoto(
   DesiredCapabilities capabilities, Platform plataforma,
   String enderecoRemoto) {

     capabilities.setPlatform(plataforma);

     WebDriver driver = null;
     try {
         driver = new RemoteWebDriver(new URL(enderecoRemoto),
         capabilities);

     } catch (MalformedURLException e) {

         e.printStackTrace();
     }

     return driver;
   }

 },
 CHROME {

  public DesiredCapabilities obterCapacidadesDesejadas() {
    DesiredCapabilities capabilities = DesiredCapabilities.chrome();

    capabilities.setCapability("chrome.switches",
    Arrays.asList("--no-default-browser-check"));

    HashMap<String, String> chromePreferences = new HashMap<String, String>();
    chromePreferences.put("profile.password_manager_enabled", "false");

    capabilities.setCapability("chrome.prefs", chromePreferences);
    // Fim

    return capabilities;
  }

   public WebDriver obterObjetoWebDriver(DesiredCapabilities capabilities) {
      return new ChromeDriver(capabilities);
   }

    public WebDriver obterObjetoWebDriverRemoto(
     DesiredCapabilities capabilities, Platform plataforma,
     String enderecoRemoto) {

       capabilities.setPlatform(plataforma);
       WebDriver driver = null;
       try {
         driver = new RemoteWebDriver(new URL(enderecoRemoto),
         capabilities);
       } catch (MalformedURLException e) {

          e.printStackTrace();
       }

       return driver;
    }
}
Listagem 3. Código do Enum TipoDriver

A seguir temos alguns detalhes importantes sobre esse código:

  • Linha 5: temos o método que retorna as capacidades para usar o Firefox;
  • Linha 10: temos o método que retorna uma instância do Driver do Firefox para uso;
  • Linha 15: temos o método que retorna um objeto remoto;
  • Linha 19: Define o sistema operacional;
  • Linha 23: Atribuição da instância do objeto remoto WebDriver, com o retorno na linha 31;
  • Linha 37: temos o método que retorna as capacidades para usar o Chrome;
  • Linhas 40 a 47: temos neste bloco a definição de alguns comandos para o Google Chrome, pois não queremos aquela pergunta de definição do navegador padrão e nem que o gerenciador de senhas pergunte se gostaria de salvar os dados de login cada vez que um teste executar uma ação de login;
  • Linha 52: método que retorna uma instância do Driver do Chrome para uso;
  • Linha 56: método que retorna um objeto WebDriver remoto.

Note que ao lado do nome do Enum TipoDriver existe a definição implements EstabelecerDriver: isso quer dizer que o Enum TipoDriver está assinando um contrato com a interface EstabelecerDriver e, como consequência, irá definir todos os métodos declarados na interface. Contudo, conseguimos definir os Enums FIREFOX e CHROME, que serão úteis na criação das classes testes.

Partiremos para a criação de outro pacote com a estrutura semelhante à Figura 12 no diretório /projeto-maven/src/main/java. Este servirá para colocarmos a classe de apoio ConversorTemperatura, que conterá métodos de conversão de temperatura, úteis para as classes de testes.

Pacote .apiconversor
Figura 12. Pacote .apiconversor

Na Listagem 4 temos o código da classe ConversorTemperatura.java, que fornecerá os resultados de cálculos de conversão de temperatura.

public class ConversorTemperatura {
                
   private final static Integer SCALE = 6;
   private static Double valor = null;
 
   public static Double aplicarScala(Double valor) {
     // Atribui um BigDecimal que recebendo o valor de um Double e que ao
     // final é lhe aplicado um escala
     BigDecimal bd = new BigDecimal(valor).setScale(SCALE,
     RoundingMode.HALF_EVEN);
     // retorna o valor em Double
     return bd.doubleValue();
   }
 
   // Conversão do valor de Celsius para Fahrenheit
   public static String celsiusParaFahrenheit(String celsius) {
     // Atribui o valor em Double convertida de uma String
     valor = Double.parseDouble(celsius);
     // Retorna uma String convertida de um valor Double onde lhe foi
     // aplicado uma escala
     return String.valueOf(aplicarScala(1.8 * valor + 32.0));
   }
 
   // Conversão do valor de Fahrenheit para Celsius
   public static String fahrenheitParaCelsius(String fahrenheit) {
    // Atribui o valor em Double convertida de uma String
    valor = Double.parseDouble(fahrenheit);
    // Retorna uma String convertida de um valor Double onde lhe foi
    // aplicado uma escala
    return String.valueOf(aplicarScala((5.0 * (valor - 32.0) / 9.0)));
  }
 
}
Listagem 4. Código da classe ConversorTemperatura.java

O Método aplicarScala aplica a escala no valor Double, ou seja, o resultado do valor 51.12313121323123 ficará 51.123131, com no máximo seis dígitos após a vírgula.

Na Figura 13 vemos o novo pacote e as classes que precisamos criar no diretório /projeto-maven/src/main/java. Nessas classes incluiremos o mapeamento das páginas da aplicação web utilizando o Selenium WebDriver, bem como métodos úteis, isso porque criaremos os testes funcionais com base nelas.

A Listagem 5deve ser incluída na classe PaginaInicio e tem como objetivo fazer referência a página Início da aplicação (http://artigo.pe.hu).

Estrutura do pacote .fabricaobjetivopagina
Figura 13. Estrutura do pacote .fabricaobjetivopagina
public class PaginaInicio {
 @FindBy(how = How.ID, using = "conversor")
 private WebElement menuOpcaoConversor;
 @FindBy(how = How.ID, using = "temperatura")
 private WebElement menuOpcaoConversorTemperatura;
 private WebDriver driver;
 public PaginaInicio(WebDriver driver) {
   this.driver = driver;
   PageFactory.initElements(driver, this);
 this.driver.get("http://artigo.pe.hu");
}

public PaginaConversorTemperatura irParaPaginaConversorTemperatura() {
  Actions acoesAvancadasDeUsuario = new Actions(driver);
  WebDriverWait wait = new WebDriverWait(driver, 10);
  acoesAvancadasDeUsuario.moveToElement(menuOpcaoConversor).perform();
  wait.until(ExpectedConditions
  .visibilityOf(menuOpcaoConversorTemperatura));
  acoesAvancadasDeUsuario.moveToElement(menuOpcaoConversorTemperatura)
  .click().perform();

  return new PaginaConversorTemperatura(driver);
}
}
Listagem 5. Código da classe PaginaInicio.java

Na linha 2 começa o mapeamento entre o objeto WebElement com o elemento HTML que tem o id="conversor"na página web. Na linha 4 temos o mesmo tipo de mapeamento, mas com o id="temperatura".

Na linha 7 temos a implementação do construtor com a atribuição do parâmetro que tem a instância para o objeto do WebDriver corrente

O comando da linha 9 permite que os atributos mapeados nesta classe com @FindBy sejam carregados com os elementos HTML correspondentes para iniciar o navegador com o endereço web.

Na linha 14 inicializa-se o objeto Actions para uso com o WebDriver corrente, assim como a linha 15, que inicializa o objeto WebDriverWait para uso com o Webdriver corrente. No construtor da classe, além de ser definido o Webdriver, foi também definido o tempo de espera de 10 segundos. Se passar disso é lançada uma exception até que os itens do menu Conversor apareçam. Quando isso acontecer, clique no item Conversor de Temperatura.

As ações ocorrem em tempo real na aplicação web e para satisfazer o retorno do método, o objeto da classe PaginaConversorTemperatura deve ser retornado com o driver corrente.

Na classe PaginaConversorTemperatura.java precisamos incluir o código da Listagem 6, pois esta fará referência à Página Conversor de Temperatura (http://artigo.pe.hu/?pg=conv_temperatura.php).

public class PaginaConversorTemperatura {
 
  // Mapeamento entre o objeto WebElement com o elemento html que tem o
  // id="valuefromtemperature1" na página web
  @FindBy(how = How.ID, using = "valuefromtemperature1")
  private WebElement valorEntrada;
 
  // Mapeamento entre o objeto WebElement com o elemento html que tem o
  // id="selectfromtemperature1" na página web
  @FindBy(how = How.ID, using = "selectfromtemperature1")
  private WebElement webSelectDe;
 
  // Mapeamento entre o objeto WebElement com o elemento html que tem o
  // id="selecttotemperature1" na página web
  @FindBy(how = How.ID, using = "selecttotemperature1")
  private WebElement webSelectPara;
 
  // Mapeamento entre o objeto WebElement com o elemento html que tem o
  // id="valuetotemperature1" na página web
  @FindBy(how = How.ID, using = "valuetotemperature1")
  private WebElement resultado;
 
  // WebDriver
  private WebDriver driver;
 
   // Implementação do construtor
   public PaginaConversorTemperatura(WebDriver driver) {
     // Atribuição do parametro que tem instância do WebDriver, para o objeto
     // WebDriver que tem o escopo desta classe
     this.driver = driver;
     // Permite que os atributos mapeados nesta classe com @FindBy sejam
     // carregados com os elementos html correspondente
     PageFactory.initElements(this.driver, this);
     // Carrega e inicia o navegador com o endereço web
     this.driver.get("http://artigo.pe.hu/?pg=conv_temperatura.php");
   }
 
   public void preencherValorDeEntrada(String i) {
    // Comando que envia valores para o elemento html
    valorEntrada.sendKeys(i);
  }
 
   public WebElement obterValorDeEntrada() {
     // retorna objeto WebElement
     return valorEntrada;
   }
 
    public Select obterSelectDeTemperatura() {
     // Retorna um objeto Select que faz referência ao elemento html Select
     // na página web
     return new Select(webSelectDe);
    }
 
     public Select obterSelectParaTemperatura() {
      // Retorna um objeto Select que faz referência ao elemento html Select
      // na página web
      return new Select(webSelectPara);
     }
 
     public WebElement obterResultadoTemperatura() {
       // retorna objeto WebElement
       return resultado;
     }
 
}
Listagem 6. Código da PaginaConversorTemperatura.java

A classe PaginaTrabalheConosco está presente no código da Listagem 7 e tem como objetivo fazer referência a Página Trabalhe Conosco (http://artigo.pe.hu/?pg=trabalhe_conosco.php).

public class PaginaTrabalheConosco {
 
   // Mapeamento entre o objeto WebElement com o elemento html que tem o
   // id="nome" na página web
   @FindBy(how = How.ID, using = "nome")
   private WebElement nome;
 
   // Mapeamento entre o objeto WebElement com o elemento html que tem o
   // id="email" na página web
   @FindBy(how = How.ID, using = "email")
   private WebElement email;
 
   // Mapeamento entre o objeto WebElement com o elemento html que tem o
   // id="opcaoMasculino" na página web
   @FindBy(how = How.ID, using = "opcaoMasculino")
   private WebElement opcaoSexoMasculino;
 
   // Mapeamento entre o objeto WebElement com o elemento html que tem o
   // id="checkIngles" na página web
   @FindBy(how = How.ID, using = "checkIngles")
   private WebElement checkIdiomaIngles;
 
   // Mapeamento entre o objeto WebElement com o elemento html que tem o
   // id="selectOpcaoVaga" na página web
   @FindBy(how = How.ID, using = "selectOpcaoVaga")
   private WebElement selectOpcaoVaga;
 
   // Mapeamento entre o objeto WebElement com o elemento html que tem o
   // id="arquivoCurriculo" na página web
   @FindBy(how = How.ID, using = "arquivoCurriculo")
   private WebElement arquivoCurriculo;
 
   // Mapeamento entre o objeto WebElement com o elemento html que tem o
   // id="botaoEnviar" na página web
   @FindBy(how = How.ID, using = "botaoEnviar")
   private WebElement botaoEnviar;
 
   // Mapeamento entre o objeto WebElement com o elemento html que tem o
   // id="msgAposEnvio" na página web
   @FindBy(how = How.ID, using = "msgAposEnvio")
   private WebElement msgAposEnvio;
 
   // WebDriver
   private WebDriver driver;
 
   // Implementação do construtor
   public PaginaTrabalheConosco(WebDriver driver) {
   // Atribuição do parametro que tem instância do WebDriver, para o objeto
   // WebDriver que tem o escopo desta classe
   this.driver = driver;
   // Permite que os atributos mapeados nesta classe com @FindBy sejão
   // carregados com os elementos html correspondente
   PageFactory.initElements(this.driver, this);
   // Carrega e inicia o navegador com o endereço web
   this.driver.get("http://artigo.pe.hu/?pg=trabalhe_conosco.php");
 }
 
 public void preencherNome(String nomeValor) {
  // Comando que envia valores para o elemento html
  nome.sendKeys(nomeValor);
 }
 
 public void preencherEmail(String emailValor) {
    // Comando que envia valores para o elemento html
    email.sendKeys(emailValor);
 }
 
 public void marcarRadioButtonSexoMasculino() {
   // Comando que clica no elemento html
   opcaoSexoMasculino.click();
 
 }
 
 public void marcarCkeckBoxIdiomaIngles() {
    // Comando que clica no elemento html
    checkIdiomaIngles.click();
 }
 
 public void selecionarVaga(String vaga) {
   // Atribuição de objeto Select que faz referência ao elemento html
   // Select
   Select selOpcaoVaga = new Select(selectOpcaoVaga);
   // Comando que marca a opção do select que tem o texto x visível na
   // página web
   selOpcaoVaga.selectByVisibleText(vaga);
 }
 
 public void selecionarArquivoCurriculo(String urlAbsolutaDoArquivoPdf) {
   // Comando que envia valores para o elemento html
   arquivoCurriculo.sendKeys(urlAbsolutaDoArquivoPdf);
 }
 
 public void enviarMensagemDeTrabalheConosco() {
   // Comando que clica no elemento html
   botaoEnviar.click();
 }
 
 public String obterMensagemAposEnvio() {
   // Comando retorna o texto visível do elemento html
   return msgAposEnvio.getText();
 }
}
Listagem 7. Código da classe PáginaTrabalheConosco

Criação dos testes

Para os nossos testes criaremos duas classes no diretório /projeto-maven/src/test/java, conforme a estrutura vista na Figura 14.

Estrutura do pacote .testepaginas
Figura 14. Estrutura do pacote .testepaginas

A classe TestePaginaTrabalheConosco.java, presente na Listagem 8, irá executar os testes funcionais da página Trabalhe conosco no navegador Firefox do Node 1, então, certifique-se que o endereço da primeira máquina virtual está igual ao valor do parâmetro do método obterObjetoWebDriverRemoto(), que está dentro do método inicializarDriver() da classe TestePaginaTrabalheConosco. Lembre-se que os endereços dos Nodes podem ser visualizados em http://192.168.0.6:4444/grid/console (Selenium Standalone Server).

public class TestePaginaTrabalheConosco {
  // Declaração de variável
  WebDriver driver;
 
  @BeforeClass
  public void inicializarDriver() throws Exception {
    // Atribuição da instância do Enum do Tipo Firefox
    TipoDriver selecionadoTipoDriver = TipoDriver.FIREFOX;
     // Atribuição das capacidades para usar o tipo driver Firefox
     DesiredCapabilities capacidadesDesejadas = selecionadoTipoDriver
.obterCapacidadesDesejadas();

 
      driver = selecionadoTipoDriver.obterObjetoWebDriverRemoto(
capacidadesDesejadas, Platform.LINUX,
"http://192.168.0.9:5555/wd/hub");

 
      /**
* driver =
* selecionadoTipoDriver.obterObjetoWebDriver(capacidadesDesejadas);
*/

   }
 
   @AfterClass
   public void fecharDriver() {
    if (null != driver) {
     driver.quit();
    }
   }
   @Parameters({ "nomeParametro" })
   // Executa o método como teste
   @Test
   public void testeEnviarMensagemDeTrabalheConosco(String nomeParametro) {
      PaginaTrabalheConosco paginaTrabalheConosco = new PaginaTrabalheConosco(
      driver);
      // Preenche o campo nome
      paginaTrabalheConosco.preencherNome(nomeParametro);
      // Preenche o campo email
      paginaTrabalheConosco.preencherEmail("brendo10x@gmail.com");
      // Marca a opção do radio button
      paginaTrabalheConosco.marcarRadioButtonSexoMasculino();
      // Marca a opção do checkbox
      paginaTrabalheConosco.marcarCkeckBoxIdiomaIngles();
      // Seleciona a opção do select
      paginaTrabalheConosco.selecionarVaga("Suporte de TI");
      // Escolhe o arquivo pdf
      paginaTrabalheConosco
      .selecionarArquivoCurriculo("/home/brendo/curriculo.pdf");
      // Clica no botão para enviar o formulário com as informações acima
      paginaTrabalheConosco.enviarMensagemDeTrabalheConosco();
      // Retorna a mensagem informada na página conversor temperatura
      String msg = paginaTrabalheConosco.obterMensagemAposEnvio();
      // Verifica se são iguais, o resultado com o esperado
      Assert.assertEquals(msg, "Sucesso!");
  }
}
Listagem 8. Código da TestePaginaTrabalheConosco.java

No primeiro método a ser chamado na classe é anotado com @BeforeClass do TestNG. Em seguida, é atribuída a instância do Enum do Tipo Firefox e as capacidades para usar o tipo driver(Firefox).

Há a atribuição do WebDriver remoto, com as definições de que os testes funcionais desta classe serão executados no navegador Google Chrome do Node http://192.168.0.9:5555/wd/hub (1ª máquina virtual usando Linux.

Descomente o seguinte trecho de código e comente o anterior caso deseje testar esta classe no seu computador.

/**
 * driver =
 * selectedDriverType.obterObjetoWebDriver(desiredCapabilities);
 */

O último método fecharDriver() é executado, pois está anotado com @AfterClass do TestNG. Ele verifica se o driver é diferente null e se for, fecha o driver, ou seja o navegador que está em execução.

Na linha @Parameters({ "nomeParametro"}) temos a definição de parâmetro para que o método testeEnviarMensagemDeTrabalheConosco receba a partir do arquivo do testng.xml (do TestNG) declarado neste projeto.

Na Listagem 9 temos o código da classe TestePaginaConversorTemperatura, que irá executar os testes funcionais no navegador Google Chrome do Node 2.

public class TestePaginaConversorTemperatura {
private Select selectDe;
private Select selectPara;
private WebElement resultado;
private WebElement entrada;
private WebDriver driver;
private String resultadoValor;
private String resultadoCalculado;

@BeforeClass
public void inicializarDriver() throws Exception {

	TipoDriver selecionadoTipoDriver = TipoDriver.CHROME;
	DesiredCapabilities capacidadesDesejadas = selecionadoTipoDriver
			.obterCapacidadesDesejadas();

	driver = selecionadoTipoDriver.obterObjetoWebDriverRemoto(
			capacidadesDesejadas, Platform.LINUX,
			"http://192.168.0.2:5555/wd/hub");

	/**
	 * driver =
	 * selecionadoTipoDriver.obterObjetoWebDriver(capacidadesDesejadas);
	 */

}

@AfterClass
public void fecharDriver() {
	if (null != driver) {
		driver.quit();
	}
}

@Parameters({ "entradaParametro" })
// Executa o método como teste
@Test
public void testeCalcularConversorTemperatura(String entradaParametro)
		throws Exception {
	// Este teste executa como se fosse passos de usuário
	// Inicia a página inicial
	PaginaInicio paginaInicio = new PaginaInicio(driver);

	// Depois vai, da página inicial até a página Converso de Temperatura
	PaginaConversorTemperatura paginaConversorTemperatura = paginaInicio
			.irParaPaginaConversorTemperatura();

	// Estando na página Conversor de temperatura
	// Preencha o campo com o valor de entrada
	paginaConversorTemperatura.preencherValorDeEntrada(entradaParametro);

	// Atribui o valor de entrada a partir da referência do elemento html
	entrada = paginaConversorTemperatura.obterValorDeEntrada();
	// Atribui a referência do elemento html Select
	selectDe = paginaConversorTemperatura.obterSelectDeTemperatura();
	// Atribui a referência de outro elemento html Select
	selectPara = paginaConversorTemperatura.obterSelectParaTemperatura();
	// Por fim, atribui do valor do resultado a partir da referência do
	// elemento html
	resultado = paginaConversorTemperatura.obterResultadoTemperatura();

}

// Este teste só executa depois que o método
// testeCalcularConversorTemperatura executar, pois este teste depende da
// execução dele.
@Test(dependsOnMethods = { "testeCalcularConversorTemperatura" })
public void testeCelsiusParaCelsius() {

	// Esses dois primeiros métodos, selecionam a opção do elemento
	// html Select que tiver com x texto vísivel na aplicação web.
	selectDe.selectByVisibleText("Celsius [°C]");
	selectPara.selectByVisibleText("Celsius [°C]");

	// Verifica se são iguais, o resultado com o esperado (valor de entrada)
	Assert.assertEquals(resultado.getAttribute("value"),
			entrada.getAttribute("value"));

}

// Este teste só executa depois que o método
// testeCalcularConversorTemperatura executar, pois este teste depende da
// execução dele.
@Test(dependsOnMethods = { "testeCalcularConversorTemperatura" })
public void testeCelsiusParaFahrenheit() {

	// Esses dois primeiros métodos, selecionam a opção do elemento
	// html Select que tiver com x texto visível na aplicação web.
	selectDe.selectByVisibleText("Celsius [°C]");
	selectPara.selectByVisibleText("Fahrenheit [°F]");

	// Atribui o valor do atributo value do elemento html
	resultadoValor = resultado.getAttribute("value");

	// Atribui o resultado da conversão
	resultadoCalculado = ConversorTemperatura.celsiusParaFahrenheit(entrada
			.getAttribute("value"));

	// Verifica se são iguais, o resultado com o esperado
	Assert.assertEquals(resultadoValor, resultadoCalculado);

}

// Este teste só executa depois que o método
// testeCalcularConversorTemperatura executar, pois este teste depende da
// execução dele.
@Test(dependsOnMethods = "testeCalcularConversorTemperatura")
public void testeFahrenheitParaCelsius() {

	// Esses dois primeiros métodos, selecionam a opção do elemento
	// html Select que tiver com x texto vísivel na aplicação web.
	selectDe.selectByVisibleText("Fahrenheit [°F]");
	selectPara.selectByVisibleText("Celsius [°C]");

	// Atribui o valor do atributo value do elemento html
	resultadoValor = resultado.getAttribute("value");

	// Atribui o resultado da conversão
	resultadoCalculado = ConversorTemperatura.fahrenheitParaCelsius(entrada
			.getAttribute("value"));

	// Verifica se são iguais, o resultado com o esperado
	Assert.assertEquals(resultadoValor, resultadoCalculado);

}

// Este teste só executa depois que o método
// testeCalcularConversorTemperatura executar, pois este teste depende da
// execução dele.
@Test(dependsOnMethods = { "testeCalcularConversorTemperatura" })
public void testeFahrenheitParafahrenheit() {

	// Esses dois primeiros métodos, selecionam a opção do elemento
	// html Select que tiver com x texto visível na aplicação web.
	selectDe.selectByVisibleText("Fahrenheit [°F]");
	selectPara.selectByVisibleText("Fahrenheit [°F]");

	// Verifica se são iguais, o resultado com o esperado (valor de entrada)
	Assert.assertEquals(resultado.getAttribute("value"),
			entrada.getAttribute("value"));

}
Listagem 9. Código da classe de TestePaginaConversorTemperatura.java

Execução dos testes

Antes de executarmos os testes, certifique-se que os Nodes e o Hub central estão funcionando corretamente. Lembre-se que o TestNG deve estar instalado no seu Eclipse (vide seção Links).

Agora, termos que criar um arquivo de configuração do TestNG para dizermos a ele quais classes de testes serão executadas. Sendo assim, crie um arquivo chamado testng.xml no diretório /projeto-maven/src/test/resources e, em seguida cole o código da Listagem 10 no arquivo testng.xml.

<suite name="Suite de teste" parallel="tests" thread-count="2">
                
   <test name="Teste na primeira máquina">
       <parameter name="entradaParametro" value="2" />
       <classes>
         <class
         name="br.com.aprendendo.selenium.testepaginas.TestePaginaConversorTemperatura" />
      </classes>
    </test>
    <test name="Teste na segunda máquina">
     <parameter name="nomeParametro" value="Brendo Felipe" />
     <classes>
       <class
       name="br.com.aprendendo.selenium.testepaginas.TestePaginaTrabalheConosco" />
     </classes>
   </test>
</suite>
Listagem 10. Código do arquivo testng.xml

Observe que no arquivo criado declaramos na tag dois atributos importantes: o parallel="tests" e thread-cont="2". Isso significa que o TestNG fará o lançamento ou execução dos testes de forma paralela. Com isso, fizemos um “Aperfeiçoamento” e os testes serão iniciados e executados ao mesmo tempo, no respectivo navegador.

Neste momento, o nosso projeto já está pronto e agora vamos executar os nossos testes funcionais distribuídos. Para isso, primeiro selecione o arquivo testng.xml e em seguida clique com o botão direto e escolha as opções Run AsTestNG Suite.

Mostra o resultado dos testes
Figura 15. Mostra o resultado dos testes

Como mostra a Figura 15 todos os testes passaram. Assim, vimos na prática como agilizar os nossos testes e otimizar o desenvolvimento das nossas aplicações.