O Selenium se provou ser uma ótima ferramenta para auxiliar os testes em sistemas web. O desenvolvedor ganha mais confiança ao realizar alterações no sistema, os webmasters também e se preocupam cada vez mais em deixar as telas mais fáceis para automatizar e dar manutenção.
Mas à medida que o número de testes aumenta, o tempo total de execução também aumenta, e muito. A maneira de conseguir diminuir esse tempo é usando o Selenium Grid.
O Selenium Grid atua como um proxy entre as requisições dos clientes e um pool de servidores Remote Control. Com isso, o grid consegue gerenciar ao mesmo tempo todos os RCs que estão conectados. O benefício disso tudo é que você pode paralelizar a execução dos seus testes e consequentemente o tempo de execução dos seus testes diminui.
E como você consegue fazer isso?
Primeiro você tem que baixar o Selenium Grid em http://selenium-grid.seleniumhq.org/download.html. A última versão é a 1.0.3.
Descompacte o arquivo baixado em algum lugar de sua preferência.
Você também vai ter que ter o ant instalado em sua máquina. Baixe-o aqui.
Entre no diretório do Selenium Grid e rode o comando para subir o servidor principal, o hub.
O hub vai subir na porta 4444 e é onde o seu teste tem que se conectar.
Agora suba dois servidores do Selenium RC com o browser Firefox:
ant -Dport=5556 -Denvironment=*chrome launch-remote-control
ant -Dport=5557 -Denvironment=*chrome launch-remote-control
Acesse agora http://localhost:4444/console e você verá uma tela contendo os servidores disponíveis.
Pronto, você já tem a estrutura de servidores para que seus testes rodem paralelamente.
Agora vamos criar um projeto Maven para que tudo fique organizado e seja fácil disparar os testes (ao fim do post tem um link para fazer download do projeto com o exemplo). Para baixar o Maven acesse aqui.
Rode o seguinte comando para criar um novo projeto de nome “testes-em-paralelo”:
mvn archetype:create -DgroupId=br.com.seuenium -DartifactId=testes-em-paralelo
Esse comando cria uma estrutura básica de diretórios para um projeto Java. Um desses diretórios criados é o src/test/java onde o Maven por convenção vai chamar todos os seus testes que estiverem localizados nessa pasta. Essa convenção é definida pelo plugin maven-surefire-plugin.
Esse plugin consegue rodar tanto testes em Junit como em TestNG.
Para quem não conhece, o TestNG é um framework de testes praticamente igual ao Junit, porém com algumas funcionalidades a mais. Uma dessas funcionalidades é a possibilidade de rodar os seus testes em paralelo.
Para habilitar o TestNG, apenas inclua a dependência da lib do TestNG no seu pom.xml. Vai ficar assim:
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>5.8</version>
<scope>test</scope>
<classifier>jdk15</classifier>
</dependency>
Você também tem que adicionar a dependência da api cliente do Selenium:
<dependency>
<groupId>org.seleniumhq.selenium.client-drivers</groupId>
<artifactId>selenium-java-client-driver</artifactId>
<version>1.0-beta-2</version>
</dependency>
Outra configuração que você precisa fazer, é dizer ao TestNG que seus testes devem rodar em paralelo. Para isso você tem que configurar o maven-surefire-plugin.
Vai ficar assim:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<parallel>tests</parallel>
<threadCount>2</threadCount>
</configuration>
</plugin>
Você tem dois possíveis valores para a configuração “parallel”: tests ou methods.
A threadCount equivale a quantos testes vão rodar ao mesmo tempo. No nosso caso vamos usar 2 pois só subimos duas instâncias de servidores Remote Control.
Após você já ter essa estrutura do Maven criada, vamos criar os testes em Java.
Para facilitar o exemplo, criei uma tela para podermos testar tudo isso. Essa tela é uma página de busca, com um input text para você digitar um termo e o botão buscar. Ao clicar no botão o resultado da busca aparece em uma tabela. Se não há resultado aparece uma mensagem informando que não encontrou.
Vamos criar então dois testes para essa tela:
- Busca com resultado
- Busca sem resultado
Abra então a Ide do Selenium no Firefox e grave o primeiro teste. Para isso digite o termo “Selenium” e mande buscar. Faça também um verificação de sucesso. Em seguida exporte-o para Java e salve no diretório testes-em-paralelo/src/test/java/br/com/seuenium/ com o nome TesteBuscaComResultado.java.
Faça o mesmo para o segundo teste, porém usando o termo “Seuenium” e salvando o arquivo como TesteBuscaSemResultado.java.
Temos agora duas classes Java que são dois TestCases. Quando se exporta para Java, a IDE cria uma classe de teste que extende um TestCase padrão do Selenium, a SeleneseTestCase. No nosso caso, vamos alterar isso. Vamos criar o nosso TestCase padrão, uma classe abstrata, onde as outras classes de teste irão extender esse cara.
O TestCase abstrato fica mais ou menos assim:
package br.com.seuenium;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import com.thoughtworks.selenium.DefaultSelenium;
public abstract class AbstractTestCase {
protected DefaultSelenium selenium;
@BeforeTest(alwaysRun=true)
public void startSelenium() {
selenium = new DefaultSelenium("localhost", 4444,
"*chrome", "http://seuenium.com.br");
selenium.start();
}
@AfterTest(alwaysRun=true)
public void stopSelenium() {
selenium.stop();
}
}
Temos o método startSelenium() que inicializa uma sessão com o Selenium, que no nosso caso é o hub do grid. Temos também o comando stopSelenium() que fecha a sessão com o servidor do Selenium.
Colocando as anotações do TestNG @BeforeTest e @AfterTest, sempre que um teste for executado, ele primeiro vai executar o startSelenium(), em seguida vai rodar o teste e ao fim vai chamar o stopSelenium(). O parâmetro alwaysRun=true garante que mesmo que uma exceção ocorra, esses métodos serão executados.
Agora vamos para os nossos TestCases exportados pela IDE. Primeiro alteramos ele para extender a classe abstrata que acabamos de criar. Em seguida, vamos colocar a anotação @Test na própria classe, dando um valor para o parâmetro chamado “testName”. Você também precisa colocar a anotação @Test no método que contém as chamadas na api do Selenium.
O classe TesteBuscaComResultado então vai ficar assim:
package br.com.seuenium;
import static org.testng.Assert.*;
import org.testng.annotations.Test;
@Test(testName="Teste com resultado")
public class TesteBuscaComResultado extends AbstractTestCase {
@Test
public void testNew() throws Exception {
selenium.open("/testes/4/buscar.php");
selenium.type("q", "Selenium");
selenium.click("//input[@value='Buscar']");
selenium.waitForPageToLoad("30000");
assertTrue(selenium.isTextPresent("2 resultados encontrados com a palavra Selenium"));
}
}
Faça o mesmo com a outra classe TesteBuscaSemResultado .
Mas por que eu tenho que colocar a anotação @Test na classe e ainda por cima configurar o parâmetro testName?
Bom, isso eu demorei pra entender. Eu estava mais familiarizado com o Junit, onde um teste corresponde a um método anotado com @Test. Para o TestNG um teste corresponde a uma ou mais classes e o que define esse teste é o seu nome. Você até pode ter mais de um teste por classe, mas eles nunca rodarão em paralelo entre si, ou seja, todos os métodos com @Test da mesma classe rodarão sequencialmente.
Agora vamos rodar os testes em paralelo.
Na linha de comando, dentro do projeto testes-em-parelelo, digite:
Com esse comando, o maven vai compilar as suas classes e vai chamar todos os seus testes.
O resultado esperado disso tem que ser um “BUILD SUCCESSFUL”.
Mas fique atento a parte com os prints dos seus testes. Você vai ver algo parecido com:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running TestSuite
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 10.557 sec
Results :
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
Note que 2 testes foram executados e demoraram 10.557 segundos.
Só para ter garantia de que você teve um ganho no tempo de execução dos seus testes, altere no pom.xml o threadCount para 1, para que seu teste rode sequencialmente. Você vai ter um resultado parecido como esse:
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 19.402 sec
Praticamente o dobro do tempo demorou.
Bom, vimos que é possível paralelizar os testes e ganhar tempo na execução. Há também um outro carinha que paraleliza testes em Java, é o Parallel Junit, mas eu não encontrei uma integração desse cara com o maven.
Mas e se meus testes não estão escritos em Java? Há frameworks que fazem isso para outras linguagens. Em Ruby por exemplo você tem o DeepTest. Para mais informações dê uma olhada no faq do Selenium Grid, clique aqui.
O projeto Maven completo com todas as classes e tudo configurado, pronto pra rodar, você pode baixar AQUI.
Bom, é isso. Qualquer dúvida só perguntar.