No último post, vimos como instalar o Watir e escrever um teste usando ele.
Neste iremos nos aprofundar mais no Watir e para isso nada melhor do que testar.
Após a leitura do post, você estará sabendo:
- Como modelar os seus testes utilizando orientação a objetos;
- A importância de utilizar orientação a objetos nos seus testes;
- Novos métodos da API do Watir;
- Como fazer asserções de eventos assíncronos (AJAX).
Contexto
A equipe do Twitter ficou sabendo que existe uma comunidade brasileira que manja muito de Teste de Software. Então, decidiram terceirizar para nós, a automação de alguns testes do Twitter.
Embora a parte da equipe do Twitter ter feito essa solicitação não seja verdade (hehe), somos pró-ativos, não somos?
Nossa missão é testar a funcionalidade de enviar uma mensagem no Twitter, o bom e velho “tweetar”. Os cenários de teste que temos são os seguintes:
- Enviar uma mensagem válida;
- Enviar uma mensagem repetida;
- Enviar uma mensagem com mais de 140 caracteres;
- Enviar uma mensagem em branco.
Típicos testes de sistema, então vamos colocar a mão na massa!
Como iremos modelar os testes?
Lembra do nosso primeiro teste usando o Watir? Ele é simplesmente um script de teste. Podemos muito bem fazer todos os nossos testes daquela maneira. Porém, vamos usar um pouco a nossa imaginação, para entender como o nosso mundo pode estar, se seguimos essa abordagem.
Temos mais de 100 testes, todos espalhados em scripts de teste, alguns contém só algumas linhas, outros são quase uma dissertação. Todos eles são gerenciados por um script mestre, que chama os scripts de teste e vai armazenando os resultados, para depois “printar” na tela.
Eu chamo o cenário acima de CAOS.
Automatizar os testes dessa maneira, é criar uma bola de neve que só irá aumentar, aumentar e um dia ninguém mais irá consegui movimentar ela. E aí vão culpar a ferramenta que é um lixo, ou a aplicação sob teste, quando na verdade o culpado é a própria equipe, que deu maior importância na criação dos testes, mas que acabou se esquecendo de organizar/estruturar a criação deles.
Além disso, o cenário acima mostra claramente uma reivenção da roda sem nenhuma necessidade: o gerenciamento dos testes. Hoje há muitas ferramentas que podem fazer isso para nós, no ecossistema Ruby, temos o TestUnit e o Rspec, por exemplo.
Uma abordagem mais efetiva, é utilizar orientação a objetos ao modelar os testes. Desta maneira será muito mais fácil dá manutenção no código e reutilizar-lo.
Como iremos aplicar OO nos nossos testes?
Como você sabe (se não sabe, deveria saber – leia esse livro e/ou essa apostila para aprender), a orientação a objetos é uma forma de organizar o nosso código, que nos orienta a trabalhar com objetos (não dá para ficar explicando conceitos de OO neste post, pois ele iria ficar enorme e minha explicação não seria melhor que a do Matt ou do pessoal da Caelum).
No nosso caso, uma página seria um objeto. Faz sentido não faz? Afinal, uma página contém atributos (os elementos HTML) e ações (as funcionalidades que ela provê).
Todos os nossos testes sehrão feitos numa mesma página, a página home do Twitter , portanto iremos instanciar o objeto HomePage da classe HomePage.
A primeira versão da nossa classe HomePage contém os seguintes métodos:
- visit – acessa a home page do Twitter (twitter.com);
- login – fará login no Twitter;
- type_message – digita uma mensagem;
- tweet – clica no botão para enviar uma mensagem;
- message_exists? – verifica se uma mensagem existe na timeline;
- alert_message_exists? – verifica se uma mensagem existe na página;
- tweet_button_is_disable? – verifica se o botão para enviar a mensagem está desabilitado.
Falta alguma coisa, não falta não?
Os atributos da nossa classe.
Isso mesmo, mas vamos deixar eles de lado no momento, para não complicar demais o entendimento do nosso exemplo, afinal estamos começando, então vamos com calma e deixar eles para uma refatoração em um futuro post.
Você irá notar que, ao invés, de interagir com os atributos da classe HomePage, os nossos métodos estão usando o objeto browser para lidar com os elementos da página. Ou seja, abstrair os atributos da home page do Twitter, é luxo para a nossa primeira versão.
Testando o Twitter
Vamos parar com o blá-blá e ir para o código. Primeiro a classe que iremos utilizar nos nossos testes:
require 'rubygems' require 'watir' #require 'firewatir' => se você estiver no Linux/Mac class HomePage HOME_PAGE = 'twitter.com' def initialize @browser = Watir::Browser.new end def visit @browser.goto(HOME_PAGE) end def login(username, password) @browser.text_field(:name, 'session[username_or_email]').value = username @browser.text_field(:name, 'session[password]').value = password @browser.button(:class, 'submit button').click end def type_message(message) @browser.text_field(:class, 'twitter-anywhere-tweet-box-editor').value = message @browser.text_field(:class, 'twitter-anywhere-tweet-box-editor').fire_event("onMouseDown") end def tweet @browser.link(:class, "tweet-button button").click end def message_exists?(message) @browser.wait_until {@browser.div(:class, 'tweet-text').text == message} end def alert_message_exists?(message) @browser.wait_until {@browser.text.include? message} end def tweet_button_is_disabled? @browser.link(:class, "tweet-button button disabled").exists? end end
Aí está a nossa classe bonitona. Nela conseguimos abstrai várias ações que podemos fazer na página home do Twitter, desde preenchimentos até verificações de elementos.
Antes de irmos para os nossos testes propriamente ditos, vamos dá uma olhada nos métodos da API do Watir que estamos usando pela primeira vez:
- @browser.text_field(:class, ‘twitter-anywhere-tweet-box-editor’).value – no nosso primeiro teste usamos o set para preencher o text_field, agora estamos usando o value, simplesmente porque o set tem um bug no Firewatir (se você está testando usando o IE, e assim usando o Watir e não o Firewatir, não terá esse problema), e uma das formas de contornar ele, é usar o value;
- @browser.text_field(:class, ‘twitter-anywhere-tweet-box-editor’).fire_event(“onMouseDown”) – o fire_event possibilita disparar um evento num elemento. No caso nosso seria como se a pessoa estivesse com o mouse em cima do text_field para digitar a mensagem e clica nele. Precisamos fazer isso, pois só assim o botão (que na verdade é um link) para “tweetar” será habilitado, uma vez que ele depende do disparo desse evento Javascript;
- @browser.wait_until – o Watir é bem espertinho quanto a espera para carregar a página, esperando realmente ela carregar, e assim não precisamos ficar doidos adicionando sleeps. Porém ele não é mágico e com o wait_until podemos esperar a nossa mensagem aparecer na timeline, tendo ele um timeout de 60 segundos, o que é muito bom, pois não queremos esperar infinitamente. Estamos usando ele, pois a nossa mensagem aparece na timeline de forma assíncrona (AJAX), ou seja, a página não é carregada novamente, e então não podemos utilizar um @browser.text.included?(‘nossa mensagem’);
- @browser.link(:class, “tweet-button button disabled”).exists? – este método é auto-explicativo, ele verifica se o elemento existe ou não na página. Estamos usando ele, pois a forma de descobrir se o link para “tweetar” a mensagem está habilitado ou não, é através da classe css do link, que quando desabilitado também terá a classe disabled.
Feitas as devidas explicações, vamos vê como ficam os nossos testes utilizando a classe HomePage:
USERNAME = 'SEU USUÁRIO NO TWITTER' PASSWORD = 'A SUA SENHA NO TWITTER' MESSAGE = 'testando o Twitter' MESSAGE_WITH_MORE_THAN_140_CARACTERS = 'testando o limite de caracteres na mensagem no Twitter, chegou? ainda nao? nao? nao? ainda nao? quando vamos chegar? estamos perto? chegamos!' TWITTER_REPEATED_MESSAGE = 'Whoops! You already said that...' #Teste 1 - Enviar uma mensagem válida home_page = HomePage.new home_page.visit home_page.login(USERNAME,PASSWORD) home_page.type_message(MESSAGE) home_page.tweet message = 'O teste enviar uma mensagem válida' if home_page.message_exists? MESSAGE puts "#{message} PASSOU" else puts "#{message} FALHOU" end #Teste 2 - Enviar uma mensagem repetida home_page.type_message(MESSAGE) home_page.tweet message = 'O teste enviar uma mensagem repetida' if home_page.alert_message_exists? TWITTER_REPEATED_MESSAGE puts "#{message} PASSOU" else puts "#{message} FALHOU" end #Teste 3 - Enviar uma mensagem com mais de 140 caracteres home_page.type_message(MESSAGE_WITH_MORE_THAN_140_CARACTERS) message = 'O teste enviar uma mensagem com mais de 140 caracteres' if home_page.tweet_button_is_disabled? puts "#{message} PASSOU" else puts "#{message} FALHOU" end #Teste 4 - Enviar uma mensagem em branco home_page.type_message('') message = 'O teste enviar uma mensagem em branco' if home_page.tweet_button_is_disabled? puts "#{message} PASSOU" else puts "#{message} FALHOU" end
Com certeza os nossos testes ficaram bem melhor utilizando a classe HomePage, em comparação se a gente fosse fazer sem ela. No entanto, eles estão BEM RUINS, em comparação com os testes do próximo post. 😀
A intenção dos posts não é dá “a melhor” solução de bandeja, afinal o melhor da jornada da automação dos testes (e de qualquer outra jornada), é justamente o caminho, se eu mostrasse logo no primeiro post a solução final, a gente não ia entender os motivos pelos quais ela é uma solução melhor que as anteriores. Talvez até seria possível entender, mas evoluindo aos poucos, irá ficar muito mais clara a diferença desses testes porcos, para os da solução final.
De qualquer modo, já atendemos a demanda que foi solicitada pelo pessoal do Twitter (hehe). O que iremos vê no próximo post, será como melhorar o código dos nossos testes, utilizando o Rspec. Até lá!
Observações
- Disponibilizei no meu Gist, o código completo do post: http://bit.ly/ges0xH
- O código foi testado no Windows com o IE8 e também no Ubuntu usando o Firefox 3.6 (lembrando que para testar usando o Firefox, você tem que usar o firewatir e não o watir).
Saiba mais
Para saber mais sobre o Watir, acesse o meu delicious, lá estou guardando vários links a respeito do Watir, e não deixe de visitar a página oficial do projeto, lá tem vários bons exemplos e explicações.
Fique por dentro das novidades, assine o feed do QualidadeBR.