Posts com a tag ‘selenium’

Perguntas e Respostas sobre Selenium

25 de agosto de 2010

Para quem não conhece, foi criada uma área no StackExchange dedicada 100% ao Selenium. É um site de perguntas e respostas, e o grande lance é que geralmente as perguntas e respostas são de alta qualidade, já que muito expert no assunto participa.
O site ainda está na fase de commitment, e isso quer dizer que precisa atingir um certo número de usuários comprometidos a fazer perguntas e respostas para passar para a próxima fase. Quando isso acontecer, o site finalmente será liberado para perguntas e respostas. Hoje a taxa de commitment está em 22%, precisamos chegar nos 100%.
Se você acho o Selenium muito legal e tem interesse em contribuir para a comunidade, inscreva-se agora!!
http://area51.stackexchange.com/proposals/4693/selenium

O único problema é que o site é em inglês. Se você não se sente confortável em ler e escrever em inglês, tire suas dúvidas em português na parte de Dúvidas do blog.

Ajude a divulgar essa ideia!!!

Autenticação HTTP básica e Selenium

14 de maio de 2010

Recentemente, um dos leitores do blog me perguntou como automatizar um teste quando o sistema tem uma autenticação HTTP básica e achei interessante compartilhar com vocês.

Exemplo de autenticação HTTP básica

Não há nada específico para o Selenium, mas sim sobre o protocolo HTTP.
Segundo a RFC 1738 que descreve como devem ser escritas as URLs, você pode definir o usuário e senha diretamente no endereço que deseja acessar. Exemplo:

http://usuario:senha@www.seusite.com/home.html

Aparentemente, isso deveria funcionar para todos os browsers, já que é um padrão. Mas no Internet Explorer não vai funcionar. Isso porque o Windows desabilita essa autenticação por medidas de segurança. Mas como para testar no IE você tem que subir o Selenium Remote Control, isso não será um problema já que o próprio RC altera o registro do Windows habilitando essa funcionalidade. Para maiores informações você pode dar uma olhada aqui.

Portanto, no seu teste automatizado, você teria que usar o comando open e alterar a URL desejada para conter os dados de acesso:

open | http://usuario:senha@www.seusite.com/home.html

Até a próxima :)

Screenshot com Selenium – Parte 2, TestNG

23 de abril de 2010

Há um tempo atrás escrevi um post demonstrando como dar print screen com Junit, Maven e Selenium se houvesse algum erro no teste.
Recentemente, um dos leitores do blog me perguntou se era possível fazer o mesmo só que usando o TestNG.
Como eu não uso muito o TestNG, eu não sabia se isso era possível, e fui atrás da documentação do TestNG e a do plugin Surefire do maven.

Para fazer isso, você terá que extender a classe TestListenerAdapter e implementar o método onTestFailure que será invocado após qualquer exceção disparada.

Deve ficar mais ou menos assim:

import org.testng.ITestResult;
import org.testng.TestListenerAdapter;
import com.thoughtworks.selenium.DefaultSelenium;
 
public class ScreenshotListener extends TestListenerAdapter
{
 
    public void onTestFailure(ITestResult result)
    {        
        try {
 
            AbstractTestCase testCase = (AbstractTestCase) result.getTestClass().getInstances(false)[0];
 
            DefaultSelenium selenium = testCase.getSelenium();
            if (selenium != null) {
                String fileName = System.getProperty(
                        "screenshots.dir", "/tmp")
                        + "/" + result.getTestClass().getName() + "-" + result.getName() + ".png";
                System.out.println("Capturando erro em: " + fileName);
                selenium.captureEntirePageScreenshot(fileName, "background=#FFFFFF");
            }
 
        } catch (Exception e2) {
            System.err
                    .println("Não foi possível capturar o screenshot");
        }
    }
}

Outro passo necessário é configurar o plugin surefire e informar o listener a ser usado:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-surefire-plugin</artifactId>
	<configuration>
		<properties>
			<property>
				<name>listener</name>
				<value>br.com.seuenium.ScreenshotListener</value>
			</property>
		</properties>
	</configuration>
</plugin>

Pronto.

Que tal integrar esse código com o exemplo de paralelização em testes do Selenium com TestNG descritos nesse post?

Como eu sou bonzinho, já deixei isso pronto. Pode baixar o exemplo AQUI e depois rode o comando mvn clean install.

Até a próxima.

Evoluindo seus testes em Selenium com PageObjects

9 de março de 2010

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.

Tela de 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:

Tela de resultado de busca

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!!!

Capturando a tela com o Selenium e Junit

4 de janeiro de 2010

Uma das coisas que notei usando Selenium é que as vezes você fica perdido quando um teste falha.
Nem sempre a mensagem de erro retornada pelo Junit ou pelo Selenium é suficiente para entender o porquê de um teste falhar.
A imagem do browser na hora em que houver uma falha pode te auxiliar na detecção do verdadeiro erro do seu teste ou sistema.

O Selenium possuí 2 comandos para dar screenshot:

1- captureScreenshot(String fileName)

Esse comando captura toda a tela. É como se você desse um print screen com o teclado.
Ficaria mais ou menos assim:

Tela Inteira

2- captureEntirePageScreenshot (String filename, String kwargs);

Esse comando captura toda a página que está no browser.
A imagem ficaria assim:

Página Inteira

O benefício do captureScreenshot é que funciona em todos os browsers. Em compensacão, se sua página é muito grande, ou seja, você precisa usar a barra de rolagem para ver a página inteira, a imagem gerada apenas conterá a parte visível. Com o captureEntirePageScreenshot esse problema não acontece, mesmo que a página tenha scroll, a imagem final será da página completa. O contra do captureEntirePageScreenshot é que ele só funciona nativamente no Firefox. Para funcionar no IE, você precisa instalar uma ferramenta http://snapsie.sourceforge.net.

Agora que você já sabe qual o comando para tirar screenshot com o Selenium, vamos agora aprender a tirar o screenshot apenas quando seu teste em Junit der erro.
Para isso você vai ter que usar a versão 4.7 ou mais recente do Junit.

Primeiro você precisa criar uma classe que implemente a interface MethodRule. Depois você precisa implementar o método apply com a lógica de chamar o comando de capturar a tela. A classe ficaria mais ou menos assim:

package br.com.seuenium;
 
import org.junit.rules.MethodRule;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
 
import com.thoughtworks.selenium.Selenium;
 
public class ScreenshotRule implements MethodRule {
 
  public Statement apply(final Statement statement, 
               final FrameworkMethod frameworkMethod, final Object testCase) {
    return new Statement() {
 
      public void evaluate() throws Throwable {
        try {
          statement.evaluate();
        } catch (Throwable e) {
 
          try {
            ExemploTest exemploTest = (ExemploTest) testCase;
            Selenium selenium = exemploTest.getSelenium();
            if(selenium != null) {
              String fileName = "/tmp/image.png";
              selenium.captureEntirePageScreenshot(fileName, "");
            }
 
          } catch (Exception e2) {
            System.err.println("Não foi possível capturar o screenshot");
          }
 
          throw e;
        }
      }
 
    };
  }
 
}

Esse código chama o método evaluate do statement que corresponde à execução de um método anotado com @Test. Se retornar uma exceção, o comando de capturar a tela é acionado.

Em seguida, na sua classe de teste, você deve criar um atributo público do tipo ScreenshotRule e deve inicializá-lo na própria declaração e anotá-lo com @Rule.
Deve ficar mais ou menos assim:

package br.com.seuenium;
 
import junit.framework.Assert;
 
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
 
import com.thoughtworks.selenium.DefaultSelenium;
import com.thoughtworks.selenium.Selenium;
 
public class ExemploTest {
 
  private Selenium selenium;
 
  @Rule
  public ScreenshotRule screenshotRule = new ScreenshotRule();
 
  @Before
  public void start(){
    selenium = new DefaultSelenium("localhost", 4444, "*firefox", "http://www.seuenium.com.br");
    selenium.start();
  }
 
  @After
  public void stop(){
    selenium.stop();
  }
 
  @Test
  public void testErro() {
    selenium.open("/testes/7-screenshot/teste.php");
    Assert.assertEquals("Hello World", selenium.getText("id=inexistente"));
  }
 
  @Test
  public void testSucesso() {
    selenium.open("/testes/7-screenshot/teste.php");
    Assert.assertEquals("Hello World", selenium.getText("id=existente"));
  }
 
  public Selenium getSelenium() {
    return selenium;
  }
 
}

Eu criei um projeto maven pronto para ser executado. Basta fazer o download aqui.
Descompacte o arquivo, entre no diretório e execute “mvn clean install”. O testErro irá falhar e você poderá ver o screenshot em target/testErro.png.

Bom, é isso pessoal. Qualquer dúvida mandem um comentário.
Até a próxima.

Benefícios em usar CSS locators no Selenium

4 de novembro de 2009

Olá garotada, o velhinho voltou.

Desculpem-me. Estive ausente por um tempinho me dedicando a outros projetos pessoais. Gostaria de agradecer a todos que entraram em contato por email ou pelo blog nesse período de ausência. Não desanimei não, estou de volta :)

Resolvi falar sobre algo que vale a pena dar uma olhada. Vocês tirem as suas conclusões heheh!!!!

Se você não sabe o que é um locator, dê uma olhada no seguinte post: “Encontrando os elementos de uma página com o Selenium”

Os princípais benefícios em usar css locators são:

  1. Expressões sucintas
  2. Teste menos sensível à alteração de template
  3. Performance nos browsers
  4. Os webmasters conhecem bem CSS locators

Vamos tomar como base o seguinte código html que descreve o menu de uma página qualquer:

<div id="menu">
 <ul>
  <li class="first">Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
 </ul>
</div>

O locator mais comum e o mais usado é o xpath. Com ele você consegue selecionar praticamente tudo da página. Mas em muitos casos ele pode não ser o ideal. Imagine que temos que fazer um teste de verificar o texto do primeiro elemento do menu. O locator em xpath para esse caso seria:

xpath=//div[@id='menu']//li[@class='first']

O locator em xpath não ficou tão simples. Agora usando css locator ficaria assim:

css=#menu .first

Bem mais simples, mais sucinto, não? Essa é a sintaxe do css. Para mais informações sobre a sintaxe você pode dar uma olhada aqui

Falando agora do segundo benefício, imagine que surgiu a necessidade de zebrar o menu. Para quem não conhece, zebrar significa alternar as cores. No nosso exemplo, as linhas pares passam a ter cores diferentes das linhas ímpares; questões de usabilidade :) Para fazer isso, as linhas ímpares do menu passaram a ter um css novo “odd” e as linhas pares “even”. O html ficou assim:

<div id="menu">
 <ul>
  <li class="first even">Item 1</li>
  <li class="odd">Item 2</li>
  <li class="even">Item 3</li>
 </ul>
</div>

Se você usar o mesmo locator em xpath do exemplo anterior para selecionar o primeiro item do menu, não funcionaria. Isso porque o locator em xpath dá match nos textos, não levando em conta o contexto css. Se você ama xpath, você teria que alterar o locator para passar no seu teste, ficando assim:

xpath=//div[@id='menu']//li[contains(@class,'first')]

Foi necessário usar a funcão “contains” que verifica se a string do atributo class contém o texto “first’.

Por outro lado, o css locator do exemplo anterior não quebraria com esse novo html. Ele funcionaria sem modificação alguma. Muito bom!!!!!!!!!!!!!!

Outro benefício é a performance da execução das expressões em css nos browsers. O css é resolvido muito mais rápido do que as expressões em xpath, já que todos os browsers possuem suporte nativo para css.
Além disso, você já deve ter notado que seus testes demoram muito mais para serem executados no IE do que no Firefox. Isso porque o IE demora muito mais para avaliar as expressões em XPath do que o Firefox. Se você usar css locators, seus testes no IE irão ser executados mais rápido. Boa dica, hein?!

Por último, os webmasters tiram de letra a sintaxe do css locator. Além de eles conhecerem bem de CSS, eles usam bastante essas expressões para selecionar elementos da página via JQuery. Portanto, se você está quebrando a cabeça para definir o locator ideal para o seu comando, vá buscar ajuda com um webmaster.

Bom galera, é isso.

Voltarei a postar em breve também. Não desistam do velhinho do Seu Enium heehhe!!! Até a próxima e deixem seus comentários. Valeu!!!!!

Novos comandos do Selenium para gerar CPF e CNPJ

20 de maio de 2009

Um dos leitores do blog, Maurício Avellar, teve uma iniciativa muito boa.
Ele resolveu compartilhar com todos alguns comandos criados por ele que provavelmente serão úteis para outras pessoas.

Os comandos criados pelo Maurício dão a possibilidade de você inserir um CPF ou um CNPJ válidos em qualquer input na tela.

A sintaxe dos comandos é a seguinte:

| generateCNPJ | locator |
| generateCPF | locator |

O único parâmetro é o locator que deve corresponder ao input que você quer inserir o novo valor.

O arquivo de extensões do Selenium que contém os novos comandos, você pode fazer o download aqui.

Para você usá-los na IDE, siga os seguintes passos:

  1. Abra a Selenium IDE e clique em Options >> Options.
  2. Altere a opção “Selenium Core extensions (user-extensions.js)” para apontar para o arquivo js que você acabou de baixar.
  3. Aperte o botão OK.
  4. Feche a IDE e abre-a novamente para que os comandos novos sejam carregados.

Pronto, você já pode usar os comandos nos seus testes.

Muito obrigado Maurício pela sua colaboração.

Quem quiser compartilhar qualquer outro comando ou experiências só entrar em contato.

:)

Por que o nome Selenium?

25 de abril de 2009

Estava eu em casa vendo TV, pensando na vida e surgiu a dúvida do porquê o tal do Selenium se chamar selenium.

Googuei.

Selenium é o nome em inglês do elemento químico selênio.
Bom, até aí acho que todo mundo já sabia disso.
O que muitos não sabem é que o selênio é utilizado como antídoto contra os efeitos tóxicos do mercúrio.
E o kiko?

Mercúrio em inglês é mercury.

Mercury é uma empresa que faz software para QA. É a grande líder do mercado, tanto que a HP pagou $4.5 bilhões ao adiquirí-la. Que grana, né? Lógico, os softwares produzidos são pagos, e bem caro$.

Como o Selenium é opensource, ele é o antídoto para quem quer se livrar das garras da Mercury.

Hehe… mas ele não é o único antídoto, nos próximos posts vou tentar falar sobre outras alternativas opensource.

Té mais!!! :)

Testes em paralelo com Selenium Grid, Maven e TestNG

5 de abril de 2009

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.

ant launch-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:

mvn clean install

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. :)

Locators do Selenium – O Retorno

20 de fevereiro de 2009

Continuando sobre o papo de locators do post anterior, há outros 2 tipos de locators que não são tão claros na documentação do Selenium. São eles:

  • attributeLocator
  • tableLocator ou tableCellAddress

Na realidade eles não são um locator por sí só. Eles são uma composição de um locator com um determinado padrão. A idéia é facilitar a seleção de um atributo html de um elemento ou a célula de uma tabela.

O padrão para usar o attributeLocator é concatenar “@nomeDoAtributo” ao fim de um locator. Não entendeu? Vou exemplificar.
Dê uma olhada no seguinte código html:

<div id="menu">
 <ul>
  <li><a href="/">Home</a></li>
  <li><a id="link-contato" title="Acesse a pagina de contato" href="/contato">Contato</a></li>
 </ul>
</div>

Para verificar que o atributo title do link Contato está com o texto correto, utilize o seguinte comando:

verifyAttribute link-contato@title Acesse a pagina de contato

Esse comando utiliza o locator identifier concatenando @title para indicar o atributo a ser selecionado.
Você pode também usar outro locator, como por exemplo o xpath:

verifyAttribute //a[@id='link-contato']@title Acesse a pagina de contato

Ou então o locator de link:

verifyAttribute link=Contato@title Acesse a pagina de contato

O outro locator que fica obscuro na documentação é o tableLocalor. Ele funciona praticamente igual ao anterior, porém deve-se concatenar “.numeroDaLinha.numeroDaColuna” a um locator de sua preferência para encontrar uma tabela. Dê uma olhada no seguinte código html que contém literalmente ou matematicamente uma tabela:

<h1>Tabela do 7</h1>
<table>
 <tr><td>7*1</td><td>7</td></tr>
 <tr><td>7*2</td><td>14</td></tr>
 <tr><td>7*3</td><td>21</td></tr>
 <tr><td>7*4</td><td>28</td></tr>
 <tr><td>7*5</td><td>35</td></tr>
 <tr><td>7*6</td><td>42</td></tr>
 <tr><td>7*7</td><td>49</td></tr>
 <tr><td>7*8</td><td>56</td></tr>
 <tr><td>7*9</td><td>63</td></tr>
 <tr><td>7*10</td><td>70</td></tr>
</table>

Para verificar que o resultado de 7 vezes 6 é 42, use o comando:

verifyTable //table.5.1 42

Essa verificação do exemplo acima, apenas valida o texto. Não garante que realmente o valor da multiplicação está correto. O Selenium não sabe fazer contas matemáticas ehehe, a não ser que você o faça aprender, mas isso é um papo para outro post sobre como extender o Selenium.

Voltando ao tableLocator, o Selenium conta as colunas e linhas a partir de 0 (zero). Logo, se você quer verificar a primeira célula da tabela, ou seja, primeira linha e primeira coluna, concatene “.0.0″ ao locator. Se sua tabela tem um cabeçalho thead, o índice zero começa das linhas e colunas que estão dentro do mesmo.

Há apenas um pequeno problema ao usar esses locators. O botão find da IDE não os localiza na página. Quando se clica no find, você recebe um erro. O problema é que a IDE não consegue distinguir que esses locators são diferentes dos convencionais, e aplica as regras de locator que expliquei no post anterior. Mas enquanto os desenvolvedores da Selenium IDE não corrigem isso, não significa que você não tem que usá-los. Muito pelo contrário. Eles são o tipo de locator que facilita muito quando você tem que verificar o valor de um atributo ou o texto de uma célula, principalmante quando você quer dar match não no texto todo, e sim em parte dele. Em outro post explico como fazer isso.

Bom, é isso galera. Acho que cobri todos os locators do Selenium. Se eu encontrar mais algum é porque ele estava muito escondido na documentação ehehehe ….
Até a próxima.
:)