Coding Dojo Floripa

Desenvolvimento Ágil

Como usar TDD e Page Objects para construir interfaces web

Posted by Ivan Sanchez em Domingo, Abril 20, 2008

Quando o assunto é interface web, a maioria dos desenvolvedores sabe o quão complicado é escrever testes automatizados, e muita gente simplesmente acaba deixando de lado esse assunto na prática, talvez por não saber o tamanho da irresponsabilidade que isto significa. Como o Vínicius da ImproveIt já bem disse:

Desenvolver software sem testes não é apenas coisa de fanfarrão. É coisa de irresponsável. É impossível uma atividade tão complexa quanto desenvolver software ser conduzida sem testes automatizados, em uma quantidade absurda. Quer dizer, possível é, mas não é aceitável.

No caso específico de interfaces web, muitos usuários do Selenium já passaram pelo extremo da empolgação, quando em 5 minutos se instala a Selenium IDE no seu Firefox e sai gravando tudo que a sua aplicação pode fazer, e acabaram no desespero total quando a coleção de testes começa a aumentar e, mesmo depois de começar a usar o Selenium RC, manter os testes fica cada vez mais difícil a cada nova mudança. E o desenvolvimento guiado por testes por onde andou durante toda esta jornada? Provavelmente restrito a testes de unidade e integração, ou seja, sem tocar na interface com o usuário.

O engraçado é que a solução para tudo isso está bem mais perto do que a gente imagina: é a velha programação orientada a objetos, representada neste caso pelo padrão Page Objects (PO). A idéia é simples:

Representar os elementos da interface com o usuário como uma série de objetos que se comunicam entre si.

Se você prestou atenção, viu que este padrão está na documentação do WebDriver, uma outra ferramenta para automatizar interação com browsers, mas que para este post não tem muita importância, desde que você consiga conectar seus Page Objects com a aplicação real. Para provar isso, aqui vai um exemplo usando selenium-rc:

public class GoogleTest {

	private Selenium selenium;

	@Before
	public void setUp() throws Exception {
		selenium = new DefaultSelenium("localhost", 4444, "*firefox",
				"http://www.google.com/webhp?hl=en");
		selenium.start();
	}

	@Test
	public void codingDojoShouldBeInFirstPageOfResults() {
		GoogleHomePage home = new GoogleHomePage(selenium);
		GoogleSearchResults searchResults = home.searchFor("coding dojo");
		String firstEntry = searchResults.getResult(0);
		assertEquals("Coding Dojo Wiki: FrontPage", firstEntry);
	}

	@After
	public void tearDown() throws Exception {
		selenium.stop();
	}

}

Este exemplo usa dois POs bastante simples (GoogleHomePage e GoogleSearchResults):

public class GoogleHomePage {

	private final Selenium selenium;

	public GoogleHomePage(Selenium selenium) {
		this.selenium = selenium;
		this.selenium.open("http://www.google.com/webhp?hl=en");
		if (!"Google".equals(selenium.getTitle())) {
			throw new IllegalStateException("This is not the Google Home Page");
		}
	}

	public GoogleSearchResults searchFor(String string) {
		selenium.type("q", string);
		selenium.click("btnG");
		selenium.waitForPageToLoad("5000");
		return new GoogleSearchResults(string, selenium);
	}
}
public class GoogleSearchResults {

	private final Selenium selenium;

	public GoogleSearchResults(String string, Selenium selenium) {
		this.selenium = selenium;
		if (!(string + " - Google Search").equals(selenium.getTitle())) {
			throw new IllegalStateException(
					"This is not the Google Results Page");
		}
	}

	public String getResult(int i) {
		String nameXPath = "xpath=id('res')/div[1]/div[" + (i + 1) + "]/h2/a";
		return selenium.getText(nameXPath);
	}
}

Quais as vantagens desta abordagem?

É possível guiar seu desenvolvimento usando testes em cima de POs.

Imagine a interação do usuário com a aplicação e crie um modelo de objetos para representar essa interação. Verifique este modelo na forma de um novo teste. Enquanto os elementos da interface não existirem o teste vai falhar, então os crie baseando-se no modelo para fazer o seu teste passar. Escreva novos testes e faça-os passarem aos poucos, e nunca inclua algo na tela que não foi descrito na forma de testes.

É muito mais fácil manter classes do que scripts.

Não demorará muito para você começar a reutilizar seus POs e poder aplicar todos os recursos de refatoração que você já conhece. Novos testes poderão ser incluídos mais facilmente e mudanças no layout afetarão apenas POs específicos.

Então, ainda existe alguma razão para não escrever testes para a interface com o usuário? Espero que não, mas se tiver, me avise!

10 Respostas to “Como usar TDD e Page Objects para construir interfaces web”

  1. leandro said

    olá, gostei muito deste post em particular.

    tentei fazer a instalação e tudo, mas só tá me dando dor de cabeça.
    por acaso, não terias um tutorialzinho, um “how-to” pra baixar e instalar as bibliotecas?
    principalmente se for para o ubuntu.
    🙂

    valeu,
    leandro

  2. Olá Leandro,

    Tudo que você precisa para rodar o exemplo que eu postei está aqui.

    Para compilar seus testes inclua o selenium-java-client-driver.jar no seu classpath.

    Para executá-los você precisa do selenium server rodando. Para iniciá-lo use o comando:

    java -jar selenium-server.jar

    Se tiver algum problema é só avisar…

    Abraço!

  3. Leandro said

    Olá Ivan,

    Está tudo certo agora.

    Obrigado.

    Leandro

  4. Bem legal. Muito simples e útil.

    Os detalhes e a complexidade da interface ficam contidos nos POs. É muito comum mudarmos besteiras na interface e quebrar vários testes a toa.

    Valeu pela dica!

  5. William said

    Boa tarde,

    Gostei muito do artigo, gostaria de saber se poderia me ajudar a compreendendo algumas coisa.
    Como faço para compilar o exemplo dado? (GoogleHomePage e GoogleSearchResults)
    qual comando tenho que dar para executar o exemplo? O Servidor eu já consegui deixar rodando.

    Obrigado e parabéns pelo artigo.

  6. Olá William,

    Você precisa do client driver do selenium-rc para compilar os Page Objects, e para rodar basta executar o teste do jUnit (GoogleTest) na sua IDE ou usando essas instruções

    Espero ter ajudado.

    Ivan

  7. […] Como usar TDD e Page Objects para construir interfaces web […]

  8. William said

    Boa tarde Ivan,

    Muito obrigado pela dica.
    Acho que estou quase conseguindo mas falta alguma coisa, o Selenium IDE é simples, mas o Selenium RC não achei a forma correta de rodar, ainda bem que existe pessoas experiente como você para compartilhar conosco.
    No momento tenho o seguinte:
    1- Estou usando o Windows
    2- O servidor rodando pelo comando
    java.exe -jar selenium-server.jar
    Esta localizado em minha maquina na pasta:
    C:\PASTA\selenium-remote-control-1.0-beta-1\selenium-server-1.0-beta-1
    3- tenho tambem o client driver localizado em:
    C:\PASTA\selenium-remote-control-1.0-beta-1\selenium-java-client-driver-1.0-beta-1
    4- jUnit em
    C:\PASTA\junit
    5- O arquivo GoogleSearchResults.java em (Exemplo mostrado acima)
    c:\PASTA\teste

    Pergunto:
    Quais passos devo fazer para rodar o exemplo?
    É atraves de linha de comando?
    Os SET CLASSPATH já foram feitos.

    Mais um vez obrigado

  9. Para quem tentar rodar o código, a estrutura da página de resposta do Google mudou, de modo que o xpath deve ser substituído por xpath=id(‘res’)/li[” + (i + 1) + “]/h3/a

  10. Otávio Landim said

    Olá,

    Bom post, simples e prático…

    Vlw.

Deixe um comentário