Escrevendo testes melhores com o Watir

Vimos no último post sobre o Watir, como melhorar os nossos testes usando o Rspec.

Já nesse post, iremos vê como melhorar os testes que escrevemos, utilizando um helper criado pelo Jeff Morgan.

Mas antes disso, deixa eu contar uma breve história sobre os nossos testes.

O Twitter mudou e quebrou os nossos testes

Como vocês devem ter notado, o Twitter recentemente (coisa de 1/2 meses), mudou a página de login. E a alteração também afetou os ids de alguns elementos da página, como por exemplo, o do text field do login e senha.

Os posts já foram atualizados, mas o que ocorreu é interessante pra ilustrar a fragilidade dos testes que dependem da interface.

Pode parecer estranho, mas tanto testar como desenvolver front-end são tarefas complicadas, principalmente Web (só lembrar das várias tecnologias usadas numa interface Web e os vários navegadores existentes). A sensação e de que quando falamos de sistemas front-end, pisamos num chão mais gelatinoso, e qualquer mudança faz ele tremer.

Para evitar o que ocorreu, o ideal é a equipe de desenvolvimento NÃO MUDAR o id dos elementos,  afinal de contas, para o usuário tanto faz se o text field tem o id username ou login, mas para os Testadores isso importa, e muito. Aliás, para ajudar na nomeação desses ids, estabelecer um padrão pra equipe ou boas práticas pode ajudar.

Bem era isso, agora vamos falar do helper que irá melhorar os nossos testes.

Melhorando os testes

Esse helper é uma mão na roda pra quem utiliza o Watir, com ele vamos passar a escrever menos código e será mais fácil de manter os nossos testes.

O helper mágico que estou falando é o seguinte:

module WatirHelper
  # A helper class to make accessing web elements via Watir easier.
  # All methods take an identifier parameter.  This parameter is
  # an array of hashes that are used to identify an element on the
  # page.  On elements that support multiple attributes you can
  # provide multiple identifiers.
  #
  # This module assumes there is a @browser variable available.

  def self.included(cls)
    cls.extend ClassMethods
  end

  module ClassMethods
    # adds three methods - one to put data in a text field, another
    # to fetch that data, and another to return the actual text_field.
    #
    # Example:  text_field(:first_name, {:id => "first_name})
    # will generate the 'first_name', 'first_name=', and
    # 'first_text_field' methods
    def text_field(name, identifier)
      define_method(name) do
        @browser.text_field(identifier).value
      end
      define_method("#{name}=") do |_value|
        @browser.text_field(identifier).value = _value
      end
      define_method("#{name}_text_field") do
        @browser.text_field(identifier)
      end
    end

    # adds three methods - one to put data in a hidden field, another
    # to fetch that data, and a third to return the hidden field.
    #
    # Example:  hidden(:first_name, {:id => "first_name})
    # will generate the 'first_name', 'first_name=' and
    # 'first_hidden' methods
    def hidden(name, identifier)
      define_method(name) do
        @browser.hidden(identifier).value
      end
      define_method("#{name}=") do |value|
        @browser.hidden(identifier).set(value)
      end
      define_method("#{name}_hidden") do
        @browser.hidden(identifier)
      end
    end

    # adds three methods - one to select an item in a drop-down,
    # another to fetch the currently selected item, and another
    # to return the select_list.
    #
    # Example:  select_list(:state, {:id => "state"})
    # will generate the 'state', 'state=' and 'state_select_list'
    # methods
    def select_list(name, identifier)
      define_method(name) do
        @browser.select_list(identifier).value
      end
      define_method("#{name}=") do |value|
        @browser.select_list(identifier).set(value)
      end
      define_method("#{name}_select_list") do
        @browser.select_list(identifier)
      end
    end

    # adds three methods - one to check, one to uncheck and
    # a third to return a checkbox
    #
    # Example: checkbox(:active, {:name => "is_active"})
    # will generate the 'check_active', 'uncheck_active', and
    # 'active_checkbox' methods
    def checkbox(name, identifier)
      define_method("check_#{name}") do
        @browser.checkbox(identifier).set
      end
      define_method("uncheck_#{name}") do
        @browser.checkbox(identifier).clear
      end
      define_method("#{name}_checkbox") do
        @browser.checkbox(identifier)
      end
    end

    # adds three methods - one to select, another to clear and
    # another to return a radio button
    #
    # Example:  radio_button(:north, {:id => "north"})
    # will generate 'select_north', 'clear_north', and
    # 'north_radio_button' methods
    def radio_button(name, identifier)
      define_method("select_#{name}") do
        @browser.radio(identifier).set
      end
      define_method("clear_#{name}") do
        @browser.radio(identifier).clear
      end
      define_method("#{name}_radio_button")  do
        @browser.radio(identifier)
      end
    end

    # adds three methods - one click a button, another
    # to click a button without waiting for the action to
    # complete, and a third to return the button.
    #
    # Example:  button(:save, {:value => "save"})
    # will generate the 'save', 'save_no_wait', and
    # 'save_button' methods
    def button(name, identifier)
      define_method(name) do
        @browser.button(identifier).click
      end
      define_method("#{name}_no_wait") do
        @browser.button(identifier).click_no_wait
      end
      define_method("#{name}_button") do
        @browser.button(identifier)
      end
    end

    # adds three methods - one to select a link, another
    # to select a link and not wait for the corresponding
    # action to complete, and a third to return the link.
    #
    # Example:  link(:add_to_cart, {:text => "Add to Cart"})
    # will generate the 'add_to_cart', 'add_to_cart_no_wait',
    # and 'add_to_cart_link' methods
    def link(name, identifier)
      define_method(name) do
        @browser.link(identifier).click
      end
      define_method("#{name}_no_wait") do
        @browser.link(identifier).click_no_wait
      end
      define_method("#{name}_link") do
        @browser.link(identifier)
      end
    end

    # adds a method that returns a table element
    #
    # Example:  table(:shopping_cart, {:index => 1})
    # will generate a 'shopping_cart' method
    def table(name, identifier)
      define_method(name) do
        @browser.table(identifier)
      end
    end

    # adds two methods - one to return the text within
    # a row and one to return a table row element
    #
    # Example: row(:header, {:id => :header}) will
    # generate a 'header' and 'header_row' method
    def row(name, identifier)
      define_method(name) do
        @browser.row(identifier).text
      end
      define_method("#{name}_row") do
        @browser.row(identifier)
      end
    end

    # adds a method to return the text of a table data <td> element
    # and another one to return the cell object
    #
    # Example:  cell(:total, {:id => "total"})
    # will generate a 'total' method and a 'total_cell'
    # method
    def cell(name, identifier)
      define_method(name) do
        @browser.cell(identifier).text
      end
      define_method("#{name}_cell") do
        @browser.cell(identifier)
      end
    end

    # adds a method that returns the content of a <div>
    # and another method that returns the div element
    #
    # Example: div(:header, {:id => "banner"})
    # will generate a 'header' and 'header_div' methods
    def div(name, identifier)
      define_method(name) do
        @browser.div(identifier).text
      end
      define_method("#{name}_div") do
        @browser.div(identifier)
      end
    end

    # adds a method that returns the content of a <dd>
    # and another method that returns the dd element
    def dd(name, identifier)
      define_method(name) do
        @browser.dd(identifier).text
      end
      define_method("#{name}_dd") do
        @browser.dd(identifier)
      end
    end

    # adds a method that returns the content of a <dl>
    # and another that returns the dl element
    def dl(name, identifier)
      define_method(name) do
        @browser.dl(identifier).text
      end
      define_method("#{name}_dl") do
        @browser.dl(identifier)
      end
    end

    # adds a method that returns the content of a <dt>
    # and another that returns the dt element
    def dt(name, identifier)
      define_method(name) do
        @browser.dt(identifier).text
      end
      define_method("#{name}_dt") do
        @browser.dt(identifier)
      end
    end

    # adds a method that returns the content of a
    # <form> element and another that returns the
    # form element
    def form(name, identifier)
      define_method(name) do
        @browser.form(identifier).text
      end
      define_method("#{name}_form") do
        @browser.form(identifier)
      end
    end

    # adds a method that returns a the content of a
    # <frame> element and another that returns the
    # frame element
    def frame(name, identifier)
      define_method(name) do
        @browser.frame(identifier).text
      end
      define_method("#{name}_frame")  do
        @browser.frame(identifier)
      end
    end

    # adds a method that returns an image <image> element
    def image(name, identifier)
      define_method(name) do
        @browser.image(identifier)
      end
    end
  end

  def content
    @browser.text
  end

  def visit_page(page_url)
    @browser.goto(page_url)
  end

  def page_title
    @browser.title
  end

  def wait_for_page
    @browser.wait
  end
end

Os comentários feitos pelo Jeff já explicam muito bem o que esse helper faz. Mas mesmo assim deixa eu dá uma explicação rápida, sobre a magia por de trás dele: Basicamente ele facilita a definição dos elementos da página. Provendo tanto o setter quanto getter dos elementos que iremos interagir durante o teste, além de prover o objeto do próprio elemento.

Pra utilizar esse helper, basta fazer o include, o que irá fazer com que a classe HomePage “ganhe” todos os métodos dele, como por exemplo o método page_title, que irá retornar o título da página que está aberta.

Abaixo está como ficou o código dos nossos testes com a utilização desse helper.

require 'rubygems'
#require 'watir'
require 'firewatir'
require 'lib/watir_helper.rb'

module Twitter

  class HomePage
    include WatirHelper

    HOME_PAGE = 'twitter.com'

    link(:tweet_button, :class => 'tweet-button button')
    link(:tweet_button_disable, :class => 'tweet-button button disabled')
    text_field(:username, :name => 'session[username_or_email]')
    text_field(:password, :name => 'session[password]')
    text_field(:editor, :class => 'twitter-anywhere-tweet-box-editor')
    button(:sign_in_submit, :class => 'submit button')
    div(:message, :class => 'tweet-text')

    def initialize
      @browser = Watir::Browser.new
    end

    def visit
      @browser.goto(HOME_PAGE)
    end

    def login(username, password)
      self.username = username
      self.password = password
      self.sign_in_submit
    end

    def type_message(message)
      self.editor = message
      self.editor_text_field.fire_event('onMouseDown')
    end

    def tweet
      self.tweet_button
    end

    def message_exists?(message)
      @browser.wait_until {self.message_div.text == message}
    end

    def alert_message_exists?(message)
      @browser.wait_until {@browser.text.include? message}
    end

    def tweet_button_is_disabled?
      @browser.link(self.tweet_button_disable_link.exists?)
    end

  end
end

O que ganhamos utilizando esse helper, você pode estar se perguntando. Basicamente duas vantagens.

Definição mais simples dos elementos web

Ganhamos três métodos  pra cada elemento:

  • um pra setar o valor do elemento (setter): self.username =
  • outro pra retornar o valor do elemento (getter): self.username
  • e por fim um que retorna o próprio objeto do elemento: username_text_field (podemos utilizar qualquer método do objeto TextField)
  • o NOME_DO_SEU_ELEMENTO_text_field é para o caso do elemento ser da classe TextField. Se for um div, seria _div, se fosse um button seria _button e assim vai.

Manutenção mais controlada

Como você viu no começo do post, não é difícil a gente precisar dá manutenção nos nossos testes, e definindo desta forma os elementos que utilizamos, precisamos alterar apenas em um lugar eles.

Por exemplo, se a classe do div com o tweet mudou de tweet-text para apenas tweet, basta mudar a definição desse div, ficando assim: div(:message, :class => ‘tweet’)

Ambas vantagens são mais evidentes ainda, quando temos suítes maiores de testes.

Qualquer dúvida a respeito do uso do helper ou do próprio Watir, abuse e use dos comentários. Lembrando que o helper, foi disponibilizado pelo Jeff Morgan no seu GitHub.

O projeto completo deste post, está disponível no meu GitHub. Aliás, você pode vê a evolução do projeto navegando pelas tags do projeto.

Abraços!

Melhorando os testes com o Rspec

No post anterior, utilizamos o excelente Watir (estou gostando mesmo dele), para testar um pouco o Twitter.

Neste iremos melhorar o código dos testes, como foi prometido.

Após a leitura do post, você estará sabendo:

  • O que é o Rspec;
  • Como escrever os seus testes com o Rspec;
  • Porquê utilizar o Rspec é uma boa ideia.

O que é o Rspec?

O Rspec é um framework de testes de unidade, assim como o JUnit. Porém ele vai além de apenas fornece um conjunto de métodos para os seus testes, e nos fornece uma DSL para descrever os compartamentos esperados do sistema[1], ou seja, ao invés, de ter apenas “um monte” de asserts, temos “um monte” de asserts dentro de exemplos executáveis, a diferença é sútil, mas faz um boa diferença.

Watir + Rspec = HAVEFUN

Os testes do último exemplo possuem um código bem ruim. Os problemas maiores dele são:

  • Repetição de código;
  • Testes fortemente acoplados;
  • Mal organizado.

Utilizando o Rspec podemos combater esses problemas. E lembre-se, não é porquê o código é para realizar testes, que ele pode ser feito de qualquer jeito, muito pelo contrário, devemos sim seguir boas práticas, como por exemplo os princípios SOLID.

Vamos agora direto vê como ficam os testes usando o Rspec:

require 'spec_helper'

USERNAME = 'YOUR USERNAME'
PASSWORD = 'YOUR PASSWORD'
MESSAGE = 'Watir rocks!'
MESSAGE_WITH_MORE_THAN_140_CHARACTERS =  "testing characters limit. 140 characters is the  characters limit. Are you pass? not yet? not? not? when are we pass? now? We break the limit!"
TWITTER_REPEATED_MESSAGE = 'Whoops! You already said that...'

describe Twitter::HomePage do

  before(:all) do
    @home_page = Twitter::HomePage.new
    @home_page.visit
    @home_page.login(USERNAME,PASSWORD)
  end

  it "send a message when it is valid" do
    @home_page.type_message(MESSAGE)
    @home_page.tweet
    @home_page.message_exists?(MESSAGE).should be_true
  end

  it "return a error messagem when try to send a repeated message" do
    @home_page.type_message(MESSAGE)
    @home_page.tweet
    @home_page.alert_message_exists?(TWITTER_REPEATED_MESSAGE).should be_true
  end

  it "don't allow to send a message when it contains more than 140 characters" do
    @home_page.type_message(MESSAGE_WITH_MORE_THAN_140_CHARACTERS)
    @home_page.tweet_button_is_disabled?.should be_true
  end

  it "don't allow to send a message when it is blank" do
    @home_page.type_message('')
    @home_page.tweet_button_is_disabled?.should be_true
  end

end

O que achou? Bem melhor que aquela “massaroca“, não é mesmo?

E viu só como o código ficou até mais simples. Prova que fazer da forma correta, é diferente de fazer da forma complicada.

O que mudou?

Antes tínhamos um código com muito repetição, é só olhar os vários ifs e elses para perceber que algo não estava certo. Agora usando o Rspec, temos os próprios métodos dele ao nosso dispor. E como você pode vê, o nosso código é praticamente um texto corrido.

Caso essa seja a primeira vez que você vê um código usando o Rspec, segue uma explicação abaixo sobre ele:

  • Primeiro estamos fazendo require de um helper, que apenas faz require da nossa classe HomePage (mais por organização);
  • No describe estamos passando a classe que iremos testar (Twitter é o módulo e HomePage é a classe) e um bloco (tudo dentro do do até o end lá do final é bloco que estamos passando);
  • O before contém passos que serão executados antes do teste (pré-condições), no nosso caso estamos logando no Twitter, antes de todos os testes. Ou seja, ele é executado apenas uma vez, e não antes de cada teste;
  • Depois temos os nossos exemplos/testes, cada um com uma asserção no final.

O que não mudou

Os nossos testes continuam com um bom nível de acoplamento/dependência, e isso costuma não ser bom. É só observar o segundo teste, ele depende do primeiro para funcionar, caso o primeiro teste falhe, o segundo provavelmente vai falar também.

No entanto, eu acabei optando por manter desta maneira, não por preguiça, mas sim porquê vejo que se o primeiro teste falha, o segundo deve falhar mesmo.

É mais uma escolha por deixar mais simples, embora eu goste da ideia de deixar os testes “atômicos”.

Organização e execução dos testes

Como você deve lembrar, temos a nossa classe HomePage, que ainda vai continuar da forma que está.

Continuamos usando ela, só que agora não temos tudo num mesmo arquivo. Temos agora um projeto, que segue a seguinte estrutura:

  • -> lib
    • -> twitter
      • home_page.rb
  • -> spec
    • -> twitter
      • home_page_spec.rb
    • spec_helper.rb

Resumindo: na pasta lib temos a classe HomePage, que prover os métodos para facilitar a criação e reutilização dos testes, que estão na pasta spec. Ambas as pastas twitter, existem pois podemos criar testes para outro site, e separar ele em outra pasta.

Por exemplo, se a gente for testar o Google, podemos criar uma pasta google, dentro de lib e spec. E aí na primeira ficarão as classes para os nossos testes e na segunda os testes em si. Tá vendo só como fica bem organizado, mesmo quando começarmos a ter muitos arquivos e pastas.

Passada a explicação sobre a organização do nosso projeto, vamos vê o díficil comando para rodar os nossos testes. Basta digitar: rspec spec/twitter/home_page_spec.rb

Só isso mesmo. Os testes serão executados e você verá em seu terminal/prompt o resultado dos testes.

Melhor que isso, só isso:

rspec spec/twitter/home_page_spec.rb -f h > resultado.html

Agora temos o resultado dos testes numa bela página em HTML. :)

Por que usar o Rspec?

Após mostrar um exemplo sem o Rspec e outro com, deve ter ficado bem clara a diferença das duas abordagens.

Não apenas estamos evitando reiventar a roda, como estamos utilizando um roda muito bem testada e amplamente usada. O Rspec é hoje, a ferramenta número 1 para muitos desenvolvedores Ruby, e mesmo a gente não precisando de muitos recursos que o Rspec pode fornecer, como por exemplo mocks, ele atende muito bem a nossas necessidades, que são básicas. Além disso, deixa os testes muito mais claros, e assim fica mais fácil para você ou uma outra pessoa dá manutenção mais pra frente.

Lógico que ele não é a única boa opção, temos por exemplo, o TestUnit que é muito bom por sinal, e se a gente quiser deixar mais alto nível ainda os nossos testes, podemos usar o Cucumber.

A mensagem que quero passar, é que poderíamos muito bem ter continuado a escrever os nossos testes daquela forma antiga, porém há formas melhores de automatizar os nossos testes. E isso não é sobre fazer “bunitinho” pra publicar num post, e sim a respeito de fazer “bunitinho” para tornar o seu trabalho mais fácil a médio e longo prazo.

O projeto

O projeto do post está disponível no meu Github (a versão 1.1 é a desse post – o link já vai pra ela). Para você utilizar ele, basta fazer clone do projeto (git clone git@github.com:FabricioFFC/watir_tests.git – necessário ter o git instalado).

Com o projeto em mãos, instale o Bundler (gem install bundler) e depois rode o comando bundle install, para instalar o Watir e o Rspec. Não se esqueça, que caso você queira testar usando o Internet Explorer, será necessário alterar dois arquivos: Gemfilehome_page.rb. Em ambos, comente a linha require firewatir e descomente a linha require watir.

Se tiver alguma dúvida ou problema para rodar os testes, só avisar nos comentários. E sugestões, críticas e opiniões sobre o projeto são bem-vindas também.

Até a próxima.

Fique por dentro das novidades, assine o feed do QualidadeBR.

OBS.: Ainda não testei no Windows, só no Ubuntu. Mas não deve ter diferenças. Se alguém encontrar algum problema, avise por favor.

[1] http://blog.davidchelimsky.net/2007/05/14/an-introduction-to-rspec-part-i/

Testando o Twitter com o Watir

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

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.

Conheça o Watir

O objetivo deste post é fazer uma introdução a ferramenta Watir (mesma pronúncia de water). Como sendo uma introdução, irei abordar o básico do básico, até porque a minha experiência com o Watir são de menos de 1 hora.

Ao terminar a leitura deste post, você estará sabendo:

  • O que é o Watir
  • Porquê usar o Watir
  • Como instalar o Watir
  • Realizar um teste básico com o Watir

O que é o Watir?

Primeiro a resposta curta de quem não conhece muito: Um Selenium desenvolvido em Ruby, cujo seus testes também serão escritos em Ruby.

Agora uma resposta apoiada no conteúdo da página oficial do Watir: O Watir é uma ferramenta para você testar aplicações Web, sob a licença BSD. Ela é um conjunto de bibliotecas em Ruby que permite você automatizar os seus testes. Você escreve os seus testes em Ruby e pode rodá-los no IE, Firefox, Chrome, Safari e Opera.

Ou seja, a minha primeira resposta é válida, porém está longe de ser a mais completa. No entanto, de início vamos com ela, afinal só iremos ter uma melhor resposta usando o Watir. :)

Por que usar o Watir?

Os motivos para usar o Watir são [1]:

  • É uma ferramenta open source e não há custos para usar;
  • A comunidade do Watir está crescendo e é muita ativa;
  • Ele usa Ruby, uma linguagem de script moderna e completa e que possui uma baixa curva de aprendizado (em comparação com Java, por exemplo);
  • Ele suporta qualquer aplicação web, não importando a linguagem na qual ela foi desenvolvida;
  • O Watir suporta múltiplos browsers e diferentes plataformas;
  • Ele é poderoso, fácil de usar e ainda é bem leve.

Como instalar o Watir

Para instalar o Watir o único pré-requisito é ter instalado o Ruby (versão 1.8.6 ou 1.8.7). Portanto vamos instalar, lembrando que os próximos passos foram feitos no Windows (se você usa Linux, vá no tópico Instalando no Linux):

  1. Instale o Ruby 1.8.7-p334 (saiba mais em RubyInstaller);
  2. Ao iniciar a instalação, logo após aceitar os termos, marque os dois checkboxes (“Add Ruby executables to your PATH” e “Associate .rb and .rbw files with this Ruby installation”) e clique em Install;
  3. Ao terminar a instalação, vá no prompt de comando (ctrl+r e depois digite cmd) e digite os seguintes comandos:
    • ruby -v (deverá retornar a versão do Ruby que foi instalada, ex: ruby 1.8.7)
    • gem -v (deverá retornar a versão do RubyGems que foi instalada, ex: 1.5.2)
    • irb (deverá entrar no irb – o shell interativo do Ruby), dentro dele escreva:
      • puts “hello world”
    • depois digite exit para sair do irb.

Se tudo deu certo, parabéns o Ruby foi corretamente instalado e de quebra você escreveu um Hello World em Ruby. ;)

Agora vamos instalar o Watir:

  1. No prompt do Windows digite: gem install watir

Pronto só isso, se nenhum erro ocorreu, já temos o Watir instalado. Caso algum erro tenha ocorrido, consulte o último tópico Erros comuns.

Realizando um teste com o Watir

Segue abaixo, um screencast mostrando como realizar um teste simples com o Watir (assistir em 720p é recomendado). Estarei fazendo o teste usando o irb:

O teste executado foi baseado no exemplo do site oficial do Watir, para vê outros exemplos acesse essa página.

Você pode fazer o mesmo teste na sua máquina, de duas formas:

  • Pelo irb, como fiz no vídeo, informando cada “comando” no irb;
  • Ou de uma forma mais fácil, baixando esse arquivo, que contém todo o script de teste completo. Além dos código que foi informado no irb, tem também vários comandos e puts, para facilitar o entendimento do código. Lembrando que após baixar o arquivo, é só executar o watir_example.rb, que um prompt abrirá e nele serão “printados” os passos e também o resultado do teste.

Conclusão

Como pudemos ver no vídeo, o Watir interage diretamente com o browser. Nós só precisamos conhecer a API dele, para podermos criar os nossos próprios testes, e é claro também um pouco de Ruby irá ajudar.

Resumindo, o que precisamos saber para criar o nosso próprio teste usando o Watir é o seguinte:

  • O valor da propriedade name dos elementos que iremos ter que interagir para poder fazer os passos do nosso teste (o desenvolvedor pode passar essa informação para você, ou você pode encontrar usando o Firebug);
    • Numa rápida pesquisa, parece que existem IDEs de record-play (estilo o Selenium IDE) para o Watir, porém nenhuma é mantida oficialmente.
  • A API do Watir, basicamente conhecer quais são os métodos que podemos usar, quando usar e quais parâmetros passar;
  • A linguagem Ruby, mas calma não precisa aprender toda a linguagem de uma vez, vá com calma, comece primeiro com exemplos básicos (como esse que mostrei) e vá incrementando eles.

Bem é isso. Qualquer dúvida, crítica ou sugestão, sintam-se à vontade em deixar nos comentários.

Instalando no Linux

Para instalar o Ruby, Rubygems e Watir no Linux, preparei um script (testado no Ubuntu). Basta baixar ele, alterar a permisão (chmod +x watir_installation.sh) e rodar o script com sudo (sudo ./watir_installation.sh).

Uma coisa que muda no script do teste, é que ao invés, de fazer o require no watir, você irá fazer o require no firewatir (require firewatir), que é o driver para o Firefox. Além disso, você terá que instalar esse plugin no Firefox. Maiores detalhes, é só conferir o procedimento de instalação que está disponível no site do Watir (lá tem para quem usa Mac também).

Erros comuns

Erro: ERROR:  While executing gem … (Errno::ENXIO)

Solução:

  • Vá no diretório C:\Ruby187\bin (ou no local que você especificou para instalar o Ruby na sua instalação);
  • Faça uma cópia do arquivo gem (para fazer backup);
  • Edite esse mesmo arquivo (gem), alterando o seu conteúdo para o que está aqui.

Fique por dentro das novidades, assine o feed do QualidadeBR.

Fonte:

[1] http://watir.com/

Pesquisa: Qual sistema operacional você usa?

Pessoal,

Estou planejando voltar a fazer alguns posts a respeito de ferramentas e para isso é importante saber de vocês queridos(as) leitores(as) qual sistema operacional vocês usam.

Para saber isso, criei uma enquete (abaixo). Você pode marcar todos os SOs que você tem afinidade, por exemplo: se você usa Windows no trabalho e Ubuntu em casa, pode marcar Windows e Linux.

A pesquisa vai estar acontecendo até essa sexta-feira (04/03).

Fique por dentro das novidades, assine o feed do QualidadeBR.

Quais ferramentas podem apoiar o Teste de Software?

A 24ª edição da Mesa Redonda DFTestes teve como tema “Quais ferramentas podem apoiar o Teste de Software?”. A discussão teve 14 respostas e 10 participantes, sendo eles: eu, Regiane Ribeiro, Rafael Silva, Rodrigo Almeida, Felipe Silva, Shmuel Gershon, Sarah Pimentel, Elias Nogueira, Ueslei Aquino e Humberto Barboza.

Abaixo faço um “resumo” da discussão, quem participa do grupo DFTestes pode acessar a discussão na íntegra, neste link.

Quais ferramentas podem apoiar o Teste de Software?

O Shmuel Gershon fez um comentário bem interessante:

A ferramenta mais útil em meu trabalho como testador é o quadro-branco (é, aquela lousa na parede mesmo, link).
Ferramentas são essenciais:
A menos que o testador tenha algum super poder de manipulação de bits e transistores com a forca da mente, ele vai precisar de um teclado, um mouse, um sistema operacional. Não dá pra testar sem ferramentas.
A pergunta é “quanto vamos deixar a ferramenta guiar o processo?”
E aí entram todas as ferramentas — as de geração de dados, as de controle de versão, as de gestão de testes — quanto nos deixamos a ferramenta decidir por nos?
Minha opinião é que se em algum momento você toma alguma ação por que é assim que a ferramenta faz, a ferramenta já está passando do seu limite. Ela trabalha pra você, não o inverso.

Segundo a Sarah Pimentel:

Qualquer uma que torne sua vida mais fácil, lembrando o que o Shmuel falou: A ferramenta trabalha pra você e não o inverso.

Eu concordo plenamente com o Luke Skywalker, que dizer Shmuel! :)

E acho que o ponto que ele  colocou, mostra bem um dos motivos pelos quais muitos profissionais ainda usam planilhas. Afinal, a planilha é totalmente customizada para a sua necessidade, você não tem que ficar preenchendo campos com blá-blá.

Quando optar pelo uso de uma ferramenta, ao invés, de um processo manual?

De acordo com a Sarah Pimentel:

- Quando o processo manual for difícil de manter/executar;
- Quando houver planejamento (tempo para aprender a ferramenta, tempo para verificar necessidades de adaptações na ferramenta e realizá-las…);
- Quando o processo de teste estiver maduro (mas ele pode amadurecer usando ferramentas também).

Para o Elias Nogueira precisamos ter o controle sobre o processo antes de buscar ferramentas:

Quando você já tem todo o controle sobre o processo e onde a aplicação dessa ferramenta não traga riscos ou grandes adaptações ao processo de teste

O Ueslei disse que:

Sabemos que em cada uma das fases do ciclo de vida, diversas são as atividades e técnicas a serem realizadas afim de gerar os produtos de cada uma das fases e, com toda certeza cada uma delas podem ser facilitadas com o uso de ferramentas. Mas, um fator que vejo de extrema importâncias é:

O profissional de teste precisa primeiro entender as técnicas de teste para então entender quais ferramentas devem ser usadas para cada técnica.

Como colocado pelo Elias Nogueira, eu reforço: O emprego de uma ferramenta errada na vez de ajudar se torna um grande risco para o projeto.

Então a melhor ferramenta é aquela que melhor atende ao processo de teste da organização e nem sempre, a ferramenta ideal para uma organização será ideal para outras. Com isso, um fator de importância é o estudo de viabilidades das ferramentas antes de sua aquisição e implantação de uma ferramenta na Organização. Esta prática pode eliminar o risco de, apenas no meio do projeto, descobrir que a ferramenta empregada não atende.

De acordo com o Humberto Barboza:

O uso de ferramentas sempre foi mesmo motivo de discussão na nossa área. Em relação a ferramentas de teste automatizado, alguns pontos devem ser questionados quando pensamos nisso. Por exemplo, uma ferramenta faz automaticamente tudo que fazemos manualmente, ou seja, ela não têm capacidade intelectual, então, o ganho de sua utilização está no tempo de execução. Porém, precisamos saber se fatores como: custo da ferramenta, prazo (Tempo de aprendizado), negócio do cliente, tipos de teste (Pra regressão é uma boa), serão adequados ao seu uso.

Em relação a outros tipos de ferramenta, considero algumas muito importantes no processo de teste. As de Gestão de Defeitos (Mantis, Trac), Gerência de Configuração (ClearCase, VSS) são bastante eficientes pois têm aprendizado e manutenção relativamente fáceis. E confesso que depois de algum tempo as utilizando ficamos dependentes das mesmas. Hoje mesmo, quero voltar a utilizar o Trac ou a HP Quality Center porque na empresa atual meu processo está todo manual com planilhas excel.

Adotamos o scrum e gostaria bastante de utilizar estas ferramentas, mas, hoje tenho um problema em que a equipe de desenvolvimento prefere o quadro afirmando que a visualização das tarefas e defeitos através dele são fundamentais para o sucesso da metodologia. Então, penso em como não ter retrabalho neste cenário, visto que neste caso teremos que incluir na ferramenta e também no quadro.

Quando o desenvolvimento de uma ferramenta internamente pode ser a melhor escolha?

A Sarah Pimentel respondeu dizendo:

Essa é uma análise que deve levar em conta:
.. atendimento às necessidades;
.. custo;
.. usabilidade;
.. tempo.

Segundo o Elias Nogueira:

Quase que jamais!!!!
Atualmente existem diversas, mas diversas ferramentas para a apoiar o processo de teste.
Já vi muitos casos que as empresas criam ferramentas de bug tracker, que é o mais comum, mas a alocação de um profissional e o tempo que a ferramenta leva para ser desenvolvida não justifica o custo de até comprar e customizar uma ferramenta…
Mas, quem sou eu pra dizer que a empresa X está errada em desenvolver uma ferramenta para o apoio aos testes.

Na verdade, creio que o único momento que podemos nos ‘dar ao luxo’ de desenvolver uma ferramenta internamente é referente a automação nos níveis unitários, de integração e de sistema. Já peguei projetos onde eu não conseguia nenhuma ferramenta e tive que criar uma arquitetura automatizada para testar um sistema de mensageira.

Só uma aviso aos novatos: muito se fala em utilizar e implantar o Mantis e Testlink para dar “velocidade” ao processo e ter um ponto centralizado. Mas iniciem conhecendo os conceitos dessas ferramentas (gestão de defeitos e gestão de testes), faça manualmente e depois implemente, se necessário, uma ferramenta.

A ferramenta que deve se adequar ao nosso processo, e não o contrário!

O Humberto respondeu a pergunta dizendo:

Penso que somente quando o negócio é muito específico e a adaptação à ferramenta inviável. Mas, quase sempre nesses casos vale mais a pena continuar com o processo manual.

Sobre o que o Elias disse, eu acho que é de se considerar a possibilidade. Principalmente, em se tratando de sistemas não web. (mas esse já é um tema de outra mesa redonda)

Bem pessoal é isso. Continuem de olho na lista do DFTestes, pois sempre há assuntos bem interessantes lá, e poderão haver novas respostas nessa mesa redonda.

Fique por dentro das novidades, assine o feed do QualidadeBR.

Picture Driven Testing com o Sikuli

Esse post já era para ter saído há um bom tempo, mas só agora tomei vergonha na cara para escrever sobre o Sikuli. :)

O que é o Sikuli?

O Sikuli é um projeto sob a licença MIT, desenvolvido pelo User Interface Design Group e o MIT Computer Science and Artificial Intelligence Laboratory (CSAIL), que utiliza imagens (screenshots) para buscar padrões na interface gráfica do usuário (GUI – graphical user interface).

Ele foi criado em Python e roda sobre a JVM (Java Virtual Machine), por meio do Jython, uma implementação da linguagem Python que gera bytecode que pode ser interpretado pela JVM.

O Sikuli tem dois componentes:

  • Sikuli IDE:uma ambiente de desenvolvimento integrado (IDE) que possibilita a escrita de scriptsvisuais utilizando screenshots;
  • Sikuli Script: uma API de scripts visuais para Jython, é por meio dele que os scripts criados no Sikuli IDE, são processados.

Como o Sikuli funciona?

A imagem abaixo mostra os componentes do Sikuli e como eles se relacionam.

Como o Sikuli funciona
Como o Sikuli funciona (fonte)

Basicamente utilizando o Sikuli IDE estamos criando um script python e salvando imagens quando tiramos um screenshot. O script python já importa automaticamente o Sikuli Script, que é o responsável por toda a “mágica” do reconhecimento de imagens. E por fim, quando rodamos o script ele é interpretado pelo Jython sobre a JVM.

Para que posso utilizar o Sikuli?

Com o Sikuli é possível automatizar tudo que você vê na tela. Você pode controlar uma página web, uma aplicação desktop no Windows/Linux/Mac OS, e até mesmo uma aplicação do iphone rodando em um emulador.

O Sikuli pode ser utilizado para diversos fins, desde a automatização de tarefas de usuários até testes de interface.

Utilizando o Sikuli para executar testes

Dentre os vários usos que pode ser feito do Sikuli, um que se destaque é a possibilidade de executar uma suíte de testes de interface (GUI Testing).

A grande diferença do Sikuli, perante as demais ferramentas de automação de testes de interface, está no uso do reconhecimento da imagem (por isso o uso do termo Picture Driven Testing), ao invés, do nome do elemento, ou coordenada da tela. Além disso, a abordagem de busca de elementos por screenshots, torna fácil a aprendizagem e evita um dos grandes problemas da automação de testes de interface, relacionadas a testabilidade do sistema (ex.: elementos com nomes dinâmicos).

O tempo de criação dos testes, provavelmente será maior, comparado as ferramentas record-play (ex.: TestComplete, Selenium IDE, etc), uma vez que é necessário realizar todo o processo de criação do script, tendo pelo menos um conhecimento básico das funções do Sikuli, e ainda você necessitará ir realizando o teste manual e capturando os elementos da tela com screenshots.

Mas por outro lado, como o nosso teste é guiado por imagens, ela será mais difícil de ser quebrado, uma vez que o Sikuli realiza uma busca, de acordo com o padrão da imagem, ou seja, o elemento está em um local diferente da tela, que mesmo assim, o Sikuli o encontrará.

Além disso, o Sikuli já foi projetado para suportar testes de unidade para GUI, integrando com o Junit. Tanto que há um painel de visualização específico para os testes (View -> Unit Test ou o atalho Ctrl+U).

Um script de teste pode ter os métodos de preparação e finalização, além é claro dos seus próprios métodos:

  • setUp(): usado para preparar o ambiente de teste;
  • tearDown(): usado para voltar o ambiente de teste ao estado antes do teste;
  • com o préfixo test: esses serão os seus métodos, que representam os testes que serão feitos (ex.: testCriandoUmArquivoDeTeste);

O Sikuli também provê duas funções de asserção:

  • AssertExist: Verifica se o padrão de imagem, passado como parâmetro, existe na tela;
  • AssertNotExist: Verifica se o padrão de imagem, passado como parâmetro, não existe na tela (o contrário do AssertExist).

Abaixo, um vídeo explicando alguns comandos do Sikuli, no qual eu crio um script bem simples, que abre o notepad, digita um texto e salva o arquivo:

Impressões sobre o Sikuli

A “descoberta” do Sikuli foi feita quando eu estava avaliando algumas ferramentas para automatizar alguns testes que exigiam o uso de interface. E acabei optando por usar o Sikuli.

De início as coisas estavam indo bem e parecia que tínhamos uma boa perspectiva futura com o seu uso. Porém, nem tudo são flores, e isso é mais verdade ainda quando falamos de testes que necessitam da interação com a interface, uma vez que eles são difíceis de se manter, e facilmente quebráveis.

E mesmo com o uso da imagem como “input”, se alguma mudança no visual ocorre na aplicação, os seus testes acabam quebrando, e foi justamente isso que ocorreu comigo. Já tinha automatizado os testes de uma nova funcionalidade da aplicação, porém ela sofreu uma grande mudança visual, e isso acabou quebrando todos os testes que eu tinha feito.

Além disso, na época em que usei ele (faz uns 2/3 meses) eu não consegui descobrir como chamar uma função python de dentro do próprio teste (a minha questão ficou por um bom tempo na lista de questões do Sikuli rs). E assim, acabava tendo que chamar um script em python (que fazia algumas asserções no banco de dados), via comandos do Sikuli + imagens, ou seja, uma solução bem paliativa.

Conclusão

Tendo em vista o pouco tempo de uso, a minha conclusão não pode ser negativa, mesmo não tendo tido sucesso no uso do Sikuli.

Afinal, o Sikuli é um projeto muito interessante e bem recente. E outras pessoas conseguiram bons resultados com ele (veja aqui e aqui).

O que recomendo para você, é testar a ferramenta, afinal ela é bem fácil de se usar, e analisar bem se a automação dos testes de interface é viável para a sua realidade. ;)

Saiba mais

Segue abaixo, alguns links de onde você encontrar mais informações sobre o Sikuli, e se aprofundar mais:

Fique por dentro das novidades, assine o feed do QualidadeBR.

Teste unitário com o SQL Developer

Acaba de ser lançada a versão 2.1 do Oracle SQL Developer. Para quem não conhece, o SQL Developer é uma IDE de banco de dados, focado em Oracle, disponível para Windows, Linux e MAC OSX. Mas também é possível conectar em outros bancos de dados, como por exemplo, MySQL. Por ela você pode gerenciar os objetos do banco de dados, rodar comandos e scripts SQL e editar e debugar comandos PL/SQL.

Na versão 2.1 há uma funcionalidade nova muito interessante: testes unitários.

Com ela é possível:

  • Criar e atualizar testes;
  • Adicionar suítes de teste;
  • Gerar relatórios de teste (ex. cobertura do código);
  • Gerenciar lookups;
  • Gerenciar a preparação e limpeza do ambiente.

A funcionalidade de testes unitários prover um framework para teste de objetos PL/SQL, como por exemplo, funções e procedures, e ainda é possível monitorar os resultados.

O fluxo de criação dos testes é formado por informar os dados do que será testado e qual o resultado esperado, ou seja, muito parecido com o fluxo de qualquer criação de teste.

Interface da funcionalidade de teste unitário

Interface da funcionalidade de teste unitário

Eu ainda não brinquei com esta nova funcionalidade, mas assim que puder informo vocês. Por enquanto, para quem se interessou e deseja saber mais, recomendo baixar o Oracle SQL Developer (é claro) e dá uma olhada neste tutorial (em inglês), explicando como realizar um teste unitário utilizando o Oracle SQL Developer. Segue os links:

Download do Oracle SQL Developer

Tutorial – Performing a Unit Test of Your PL/SQL in Oracle SQL Developer 2.1 (em inglês)

Fique por dentro das novidades, assine o feed do QualidadeBR.

Fonte:

What is SQL Developer? – Oracle

Oracle SQL Developer 2.1: New Feature List – Oracle

Imagem

Performing a Unit Test of Your PL/SQL in Oracle SQL Developer 2.1 – Oracle

Tutorial Eventum (parte 3)

Chegamos a terceira e última parte do tutorial da ferramenta bug tracking Eventum. Nesta última parte, iremos aprender  mais sobre a sua utilização, vendo na prática, por meio de vídeos, o seu uso. Aperte os cintos, pois a última parte do tutorial do Eventum irá decolar agora. Terminamos a segunda parte do tutorial, com a criação de um projeto. Agora que já temos o nosso próprio projeto pronto, é hora de aprender como é a criação de uma issue. Mas antes de começar a criar a issue, quero frisar que o reporte de um bug é uma tarefa muito importante. O Tester deve buscar ser detalhista ao máximo, e descrever todos os passos que foram executados para encontrar o bug. De nada adianta uma ferramenta de bug tracking, se o reporte do bug for superficial. Devemos sempre lembrar, que um bom gerenciamento de bugs, depende muito mais das equipes de Testes e Desenvolvimento, do que da ferramenta propriamente dita, pois como já diria um ditado popular americano, tolos com ferramentas ainda são tolos“.

Reportando uma issue

O reporte de uma issue é bem fácil de fazer no Eventum. O primeiro passo a ser dado é clicar sobre o link ‘Create Issue‘, que se encontra no menu principal. A seguinte página irá abrir.

Create Issue

  • Category - aqui serão listadas as categorias criadas anteriormente. Selecione a categoria na qual o bug foi encontrado;
  • Priority - defina qual a prioridade do bug;
  • Assignment - selecione os responsáveis pela issue, lembrando que eles irão receber um e-mail, de notificação do cadastro da issue;
  • Scheduled Release - campo opcional, usado para estimar em qual release sairá a correção;
  • Summary - neste campo coloca-se o título da issue. Uma dica é tentar se o mais específico possível, pois facilitará a busca pelas issues;
  • Initial Description - aqui você irá descrever a issue, costuma- se colocar os seguintes dados, quando necessários: pré-condição, cenário (o passo a passo), resultado inválido, resultado esperado e observações;
  • Estimated Dev. Time - campo opcional, usado para estimar as horas de desenvolvimento necessárias para a correção da issue;
  • Private - caso marcado ‘Yes’ os usuários com papel Reporter não irão ver a issue;
  • Files - você pode fazer o upload de algum arquivo que prove a ocorrência do bug, por exemplo, um printscreen.

Se após a criação da issue, houver a necessidade de colocar alguma nova informação, podemos fazer essa adição de duas maneiras: atualizando a issue ou colocando um ‘Post Internal Note’. A primeira é usada mais, quando alguma informação cadastrada foi colocada incorretamente, e a segunda maneira serve para fazer algum comentário sobre a issue, como por exemplo: O bug continua ocorrendo na versão X.

Fechando uma issue

O melhor momento é esse, e se o cadastro de uma issue já foi fácil, o fechamento dela é mais ainda. É preciso ir na página da issue. Nela há um botão chamado ‘Close Issue‘, clicamos nele. A página seguinte irá aparecer.

eventum_11

  • Status – informe qual o status do fechamento. Lembrando que você pode cadastrar os seus próprios status na página de administração;
  • Resolution - selecione qual foi a resolução tomada para a solução da issue;
  • Send Notification About Issue Being Closed - boas notícias são sempre bem-vindas, portanto escolha a opção ‘Yes’ para avisar a todos sobre o fechamento da Issue;
  • Send Notification To - aqui você pode definir se todos irão receber a notificação, ou apenas os responsáveis pela issue;
  • Reason for closing issue - descreva qual foi a razão do fechamento da issue;
  • Time Spent - campo opcional, usado para colocar o tempo que foi gasto na resolução da issue;
  • Time Category - campo opcional, para informar onde foi gasto o tempo para solucionar a issue.

Abaixo, se encontra um vídeo mostrando a criação e o fechamento de uma issue:

Para o download do vídeo com qualidade máxima, clique no link abaixo: http://www.mediafire.com/?yz25t0zuiwn (6,49 MB)

Criação de um campo customizado

Um dos diferenciais do Eventum é a possibilidade de criar um campo customizado, pois muitas vezes, há alguma informação específica de um projeto, que precisa ser colocada na issue, e acabamos colocando no campo de descrição, o que não é a solução ideal. Para criar um campo customizado é necessário está logado com um usuário ‘Administrator’, e clicar sobre o link ‘Administration‘. Na página de administração clicamos sobre o link ‘Manage Custom Fields‘ que se encontra na parte de ‘Configuration‘. A página abaixo, irá aparecer.

Create Custom Fields

Na criação do campo customizado, precisamos preencher os seguintes campos:

  • Title - defina um nome para o seu campo;
  • Short Description - defina uma descrição breve do campo, ela ficará localizada ao lado do campo;
  • Assigned Projects - escolha em quais projetos esse campo customizado estará presente;
  • Target Forms - defina em qual parte do formulário o campo estará e se ele será ou não obrigatório:
    • Report Form - no formulário de criação da issue;
    • Anonymous Form - no formulário de anonymous, que é aquele que vimos na segunda parte do tutorial;
    • Close Form - ou no formulário de fechamento da issue.
  • Display on List Issues Page - se ele aparecerá ou não na listagem de issues;
  • Field Type - qual será o tipo do campo;
  • Field Options - dependendo do tipo do campo escolhido, será necessário preencher os dados desse campo;
  • Minimum Role - a partir de qual tipo de usuário, o campo poderá ser visto;
  • Rank - em qual posição o campo customizado estará, perante os demais campos customizados.

Caso tenha ficado alguma dúvida, durante a criação do campo customizado, confira a criação de um campo customizado, no vídeo abaixo:

Para o download do vídeo com qualidade máxima, clique no link abaixo: http://www.mediafire.com/?ukmyvaycymh (2,4 MB)

Envio de e-mail automático

Antes de ver como configurar o envio de e-mail automático, é bom saber que para o usuário receber o e-mail, ele precisa habilitar essa opção, nas suas preferências. Para isso vá em ‘Preferences‘, ao lado do nome do usuário. Na nova página precisamos marcar a opção ‘Yes‘ para as perguntas: ‘Receive emails when all issues are created’ e ‘Receive emails when new issues are assigned to you’. Infelizmente, sempre que você criar um projeto novo, os usuários que participarão do projeto, estaram configurados para não receber os e-mails. Eu ainda não encontrei um meio “menos macaco”, para a solução desse problema. Até perguntei no fórum da MySQL, mas por enquanto nada.

Um meio de contornar esse problema, é enviar um e-mail para todos os usuários do projeto, pedindo a configuração para o recebimento de e-mail do projeto criado no Eventum.

Outra notícia ruim: o Eventum não tem suporte a servidores SMTP usando SSL, ou seja, a maioria dos servidores que poderíamos usar (como o Gmail), não poderão ser usados. Mas agora uma notícia boa: nós temos o nosso próprio servidor SMTP, o XMail, que configuramos na primeira parte deste tutorial, durante a instalação do Eventum.

Bem, vamos para a configuração do envio de e-mail automático. Você pode está se perguntando: o Eventum já não manda e-mail automaticamente? E a resposta é não. Para o Eventum enviar os e-mails é preciso executar um script o ‘process_mail_queue.php’ (que se encontra na pasta misc).  No linux você pode usar o Cron, para fazer essa tarefa. E no Windows, precisamos usar o agendador de tarefas. Abaixo, segue o passo a passo, para a criação da tarefa agendada no Windows. Vá em: Iniciar > Todos os programas > Acessórios > Ferramentas do Sistema > Tarefas agendadas. A seguinte tela abrirá.

Tarefas agendadas

Na nova tela, clique sobre a opção ‘Adicionar tarefa agendada’ e na tela que abrirá clique sobre o botão ‘Avançar’. Depois selecione o navegador, de sua preferência, para executar a tarefa, e clique em ‘Avançar’.

Selecionar o programa a ser executado

Agora informe o nome da tarefa e selecione a opção ‘Diariamente’, para que a tarefa seja executada todos os dias.

Coloque o nome da tarefa e selecione a opção diariamente

Na próxima tela, coloque o horário de início da tarefa, selecione a opção ‘Todos os dias’ e clique no botão ‘Avançar’.

Coloque a hora de inicio da tarefa e selecione a opção 'Todos os dias'

Agora informe a senha do usuário que executará a tarefa.

Informe a senha do usuário

Antes de concluir a tarefa, marque o check box, pois será necessário configurar algumas opções da tarefa. Marcado o check box, clique no botão ‘Concluir’.

Marque o check box

Agora estamos nas propriedades da tarefa. Na primeira aba ‘Tarefa’ acrescente “localhost/eventum-2.1.1/misc/process_mail_queue.php” (com as aspas mesmo), ao comando. Ficará assim o comando no campo ‘Executar’: C:\ARQUIV~1\MOZILL~1\firefox.exe “http://localhost/eventum-2.1.1/misc/process_mail_queue.php&#8221;. Ou seja: Endereço do navegador [espaço] endereço do script entre aspas.

Acrescente o endereço do script no comando

Agora vá na aba ‘Agendar’ e clique sobre o botão ‘Avançado’. Aqui marcamos o check box ‘Repetir a tarefa’. Definimos o intervalo de tempo que ela será repetida, colocando 5 minutos, e até que horas ela será executada, até às 23:59. Por fim, marcamos o check box ‘Se a tarefa estiver em execução, interrompê-la nesta hora.’, e clicamos no botão ‘Ok’.

Configure a repetição da tarefa

Vamos agora na aba ‘Configurações’ e marcamos apenas o check box ‘Interromper a tarefa se ela for executada por’, configurando em 2 minutos.

Informe o tempo máximo de execução da tarefa

Depois clique sobre o botão ‘Aplicar’. Uma nova tela abrirá, pedindo a sua senha e confirmação da mesma. Pronto, já temos nossa tarefa agendada concluída corretamente.

tarefas_agendadas_09

A tarefa criada irá executar o script do Eventum, para envio dos e-mails, a cada 5 minutos. Portanto, após a criação de uma issue, o e-mail será enviado no tempo máximo de 5 minutos. Para verificar se o e-mail foi enviado com sucesso, vá na página da issue que se deseja consultar, e clique sobre o link ‘Mail Queue Log‘ (localizado na parte inferior direita da página).

Verificação se o e-mail de notificação foi enviado

Uma nova página abrirá mostrando o status do envio do e-mail.

Status do envio de e-mail

Aqui encerro a terceira e última parte do tutorial sobre o Eventum. Espero que ele possa ajudá-lo(a), caro leitor(a). Qualquer dúvida, crítica, ou sugestão, sinta-se à vontade em colocar nos comentários. Até a próxima!

Fique por dentro das novidades, assine o feed do QualidadeBR.

Saiba mais

Quem quiser conhecer mais sobre a ferramenta, recomendo a wiki do Eventum. Caso tenha algum problema ou dúvida, também há o fórum da MySQL, o FAQ, a lista de discussão,  e o QualidadeBR (hehehe).

Tutorial Eventum (parte 2)

Chegamos a segunda parte do tutorial sobre o Eventum. E tenho uma novidade: o primeiro vídeo do QualidadeBR. Eu estava pensando, em como passar melhor as informações, e como nessa segunda parte do tutorial iremos aprender como trabalhar com o Eventum. Muita coisa fica difícil de entender e de explicar, usando apenas o texto. Portanto, espero  que o vídeo possa ajudar você, caro leitor(a), e complementar este tutorial.

Bem, agora é hora da ação. Mãos a obra!

Menu principal

A interface do Eventum é bem simples. Logo na página principal, já podemos ver quais são as funcionalidades principais.

Página inicial

  • Administration - clicando aqui, você irá para a página de administração do Eventum, que será melhor detalhada mais abaixo;
  • Create Issue - neste link que você clicará quando for cadastrar uma issue, ou seja, o  problema/bug;
  • List Issues - opção de listagem das issues já existentes, também permite a busca e a ordenação das issues por vários parâmetros;
  • Advanced Search - abrirá a busca avançada, permitindo uma busca bem detalhada e por diferentes parâmetros. Você ainda pode salvar as suas buscas;
  • Associate Emails - útil para a associação manual de issues com os e-mails dos responsáveis;
  • My Assignments - apresenta as issues que estão designadas ao usuário logado;
  • Stats - apresenta uma série de gráficos sobre as issues do projeto;
  • Reports - fornece uma boa quantidade de relatórios, baseados nas issues do projeto;
  • Internal FAQ - um FAQ interno é apresentado, sendo que este necessita ser criado antes na parte de administração;
  • Help - a ajuda do Eventum, que fornecer uma boa noção da ferramenta.

Administração

Agora veremos uma das principais páginas do Eventum, a de administração. O meu intuito é apresentar as principais funcionalidades, não terá como falar de todas por dois motivos: há muitas opções, o que resultaria quase num livro; e o não uso de boa parte das funcionalidades, o famoso pareto (onde 20% do software é mais usado).

Para acessar a página de administração, basta clicar sobre o link ‘Administration ‘.

eventum_02

Toda a navegação pelas opções ocorre na parte da esquerda da página, onde elas estão listadas. Na parte à direita, é feita toda edição da configuração desejada. As principais configurações são:

  • General Setup – aqui você poderá mudar as configurações de envio de e-mail, aquelas que você fez na instalação, poderão ser alteradas aqui. Há ainda a opção de configuração de outras funcionalidades, como as dicas diárias (Daily Tips) e a integração SCM (Source Code Management - Sistema de Controle de Versão), sendo que o Eventum suporta o CVS e o Subversion (mais detalhes);

General Setup

  • Manage Custom Fields – é onde você poderá criar os seus campos customizáveis, definindo: seu nome (Title), descrição (Short Description), projetos onde serão usados (Assigned Projects), em qual formulário serão usados (Target Forms), se será ou não mostrado na lista de issues (Display on List Issues Page), o tipo do campo (Field Type), as opções do campo (Field Options), qual o papel mínimo de usuário que poderá visualizar o campo (Minimun Role); em qual posição ele estará (Rank);

  • Manage Statuses - o Eventum permite a customização dos status, possibilitando a criação, edição e deleção. Você poderá configurar os seguintes dados do estado:
    • Title – o título do seu novo status;
    • Abbreviation – a abreviação do seu status (três letras);
    • Rank – a posição do status;
    • Closed Context – caso selecionada a opção ‘Yes’, o status estará presente no formulário de fechamento da issue, caso contrário, ele estará no formulário de cadastro da issue;
    • Assigned Projects – em quais projetos o status estará presente;
    • Color - possibilita a escolha da cor do status, sendo que ela deverá estar em HTML (tabela de cores).

Manage Statuses

  • Manage Projects – nesta área você poderá criar e editar os seus projetos. Na instalação do Eventum um projeto já é criado: o Default Project. Você pode criar e configurar os seus próprios projetos. Veremos melhor a parte de criação de projetos mais a frente, aliás, o vídeo mostra a criação de um projeto;
  • Manage Users – no Eventum há sete níveis de usuário, sendo que o papel do usuário está associado ao projeto, ou seja, um usuário pode exercer papeis diferentes, dependendo de cada projeto:
    • Viewer - pode visualizar todas as issues dos projetos que ele está associado, e não pode criar ou editar issues;
    • Reporter – permite visualizar todas as issues associadas a ele, e também criar novas issues e enviar e-mail nas issues já existentes;
    • Customer - esta é uma permissão reservada para uma API de integração com o cliente, que permite que você integre o Eventum com a base de dados do seu CRM. Quando esta funcionalidade está habilitada, este papel de usuário pode acessar as issues associadas ao seu próprio cliente. Permite a criação de issues, atualização e envio de e-mails de issues já existentes (mais detalhes);
    • Standard User – o usuário padrão pode visualizar todas as issues dos projetos que ele está associado, criar/atualizar issues, e enviar e-mail e notas nas issues existentes;
    • Developer – ele é parecido com o ‘Standard User’, porém ainda permite separar os usuários que irão lidar com as issues, e a equipe de usuários que não lidar com suas próprias issues;
    • Manager – permite visualizar todas as issues dos projetos que ele é associado, criar/atualizar issues e enviar e-mails e notas nas issues existentes, Ele ainda pode, editar a área de administração do Eventum, menos a área de configuração;
    • Administrator - tem acesso total ao Eventum.

    Para criar um novo usuário, basta informar as seguintes informações: o e-mail do usuário (nele será enviado o e-mail pelo Eventum), a senha, o nome, e quais projetos que ele participará e qual será o seu papel.

Manage Users

Criando um projeto

Antes de começar a utilizar o Eventum pra valer é necessário criar um projeto antes. O Eventum é organizado por projeto e cada um pode ter suas características específicas.

Abaixo, explico cada um dos campos existentes no formulário de cadastro do projeto.

Manage Projects

  • Title – o nome do projeto;
  • Status – o estado do projeto, que pode ser ‘Active‘  (usado na criação) ou ‘Archived‘ (usado no fechamento do projeto). Como estamos criando um projeto selecionamos a opção Active;
  • Customer Integration Backend – área para configuração da integração com o CRM, citada anteriormente. Lembrando que para utilizar essa função é necessário adicionar uma classe no código do Eventum. Portanto, selecionamos a opção No Customer Integration, pois não iremos usar essa funcionalidade;
  • Workflow Backend – como o nosso projeto não tem integração, selecionamos a opção No Workflow Management no combo box;
  • Project Lead – selecionamos o líder do projeto, dentre os usuários já cadastrados. Se você só tem o usuário Admin cadastrado, basta ir na opção Manage Users, explicada anteriormente, e cadastra os usuários que fazem parte do projeto. O cadastro é bem simples;
  • Users – selecionamos os usuários que participarão do projeto;
  • Statuses – aqui escolhemos quais status serão usados no projeto;
  • Initial Status for New Issues – quando a issue é criada ela tem um status inicial, definimos ele aqui como discovery (descoberto);
  • Outgoing Email Sender Name - informamos qual o nome do remetente do envio de e-mail;
  • Outgoing Email Sender Address – colocamos o endereço de e-mail do remetente;
  • Remote Invocation – caso habilitado (Enabled) permitirá o uso de CLI pelos desenvolvedores para ver e editar as informações, sem o uso de navegador web (mais detalhes). Selecionamos a opção ‘Disabled‘;
  • Segregate Reporters – opção, que caso habilitada (Yes) que permite que os usuários com o papel ‘Reporter’ sejam capaz de ver somente as issues reportadas por eles. Selecionamos a opção ‘Yes‘.

Pronto, nosso primeiro projeto está pronto, ou melhor, quase pronto.  Ainda necessitamos configurar, pelo menos, duas características do projeto (Categories e Priorities):

  • Releases - onde são cadastrados as releases, usados durante o cadastro da issue, para definir qual versão, representa o deadline para a correção da issue. Sendo esse campo opcional;
  • Categories - precisamos definir as categorias do projeto, que podem ser entendidas como os módulos da aplicação. Por exemplo: login, cadastro de usuários, vendas, etc. Sendo o seu cadastro requerido, pois durante o cadastro de uma issue necessitamos informar em qual categoria o bug ocorreu;
  • Priorities - cadastro das prioridades. Sendo o seu cadastro requirido, pois precisamos definir qual a prioridade da issue, durante o seu cadastro. Você pode usar as mesmas prioridades do ‘Default Project’ ou criar as suas próprias;
  • Phone Support Categories - adicionar uma categoria de telefone do suporte, que aparece na edição de uma issue, na parte ‘Phone Calls’. Geralmente, é usado apenas em equipes de Suporte. O seu cadastro opcional;
  • Anonymous Reporting – permite o reporte Anonymous de issues, que na verdade, possibilita que o usuário reporte uma issue sem precisa logar no Eventum. Para isso ele precisa habilita o ‘Anonymous Reporting’ e escolher o usuário que poderá fazer isso, a categoria, a prioridade e a pessoa que será associada à issue, que será cadastrada sem o login no Eventum. Feito isso o usuário pode deslogar do Eventum, e na tela de login aparecerá uma nota avisando que ele poderá reportar issues sem realizar o login, usando uma determinada URL. Esta configuração é opcional;

Login Anonymous

Anonymous Reporting

  • Fields to display - aqui você pode definir quais campos podem ser visualizados, por quais níveis de usuário. Essa configuração é opcional;
  • Columns to display - possibilita a configuração de quais campos serão apresentados na listagem de issues. Sendo essa configuração opcional.

Essas são as configurações necessárias para criar um novo projeto. Agora com o seu projeto pronto, já poderemos cadastrar a nossa primeira issue. Porém, só na próxima parte desse tutorial, aliás será a última. Onde veremos os seguintes tópicos: reporte e fechamento de uma issue, criação de um campo customizado e o envio de e-mail automático.

Mas antes de encerrar a segunda parte, segue abaixo o vídeo que eu havia comentado no começo do post. Ele apresenta a criação e configuração de um novo projeto, espero que vocês gostem. Até a próxima!

Se você quiser fazer o download do vídeo, com qualidade máxima e sem esses zooms (tive que fazer devido a qualidade do vídeo no Youtube), segue abaixo, o link:

http://www.mediafire.com/?wdahjm3zjiy (tamanho 4,62 MB)

Fique por dentro das novidades, assine o feed do QualidadeBR.
Assine o feed

Fonte:

http://eventum.mysql.org/wiki/