Tiago e Cíntia

Tiago e Cíntia

Meus queridos leitores,

Já fazem vários meses que não posto aqui no blog, o motivo é esse: irei me casar!

E saibam que dá um trabalhão! Afinal, conseguir a felicidade eterna não é de graça né?? ;)

Quem quiser acompanhar a nossa saga, confira o blog do nosso casamento: sagasdecintiago.wordpress.com

Bem, eu continuo trabalhando para Favorpals e agora estamos utilizando Spring 2.5 MVC, que foi um imenso avanço! Quando me der uma boa folga eu escrevo sobre ele.

E além disso, estou construindo um ERP para a empresa do meu pai, e estou utilizando outras tecnologias interessantíssimas como JBoss Seam e RichFaces. Também quando puder eu escrevo sobre isto.

Bem, é isso aí! Fiquei com Deus, um grande abraço!

Resumo

Ainda hoje, SPAM é um problema altamente relevante. Desenvolvedores vêm tomando diversas medidas para tentar conter os prejuízos causados por SPAM. Hoje apresentarei reCAPTCHA, uma implementação CAPTCHA para neutralizar os ataques feitos por robôs, que ao mesmo tempo que realiza testes que apenas humanos conseguem resolver, contribui para a digitalização de livros antigos.

 


Conteúdo

  1. Introdução
  2. CAPTCHA
  3. O Projeto reCAPTCHA
  4. Como reCAPTCHA funciona
  5. Como implementar reCAPTCHA em meu site
  6. Observações

 


1. Introdução

Não é de hoje que sites e sistemas web são vitimados por spammers e robôs que visam a proliferação de suas mensagens indesejadas. Esta prática é denominada SPAM: o abuso em sistemas eletrônicos de mensagens com respeito ao envio indiscriminado em massa de mensagens indesejadas. Existem várias formas de SPAM, apesar de que sua variante mais conhecida é o SPAM por e-mail. Teoricamente, qualquer sistema disponível publicamente (por exemplo, na internet) sem medidas anti-SPAM está sujeito a receber SPAM.  

Neste cenário, vêm sendo trabalhadas algumas soluções para tentar impedir, ou ao menos diminuir o envio de SPAM. 

Hoje vou apresentar uma solução apresentada contra a ação de robôs. Robôs são softwares projetados para enviar SPAM automaticamente. Realizam tarefas como criar contas em sites, realizar login, enviar mensagens, etc. 

 

2. CAPTCHA

É sabido que robôs podem realizar ações de interação com a interface, como acessar caixas de texto, digitar texto, selecionar botões, clicar, etc. Mas há certas ações que apenas os seres humanos são capazes de realizar, como copiar texto distorcido. Se o texto não estiver devidamente distorcido, o robô pode utilizar técnicas de OCR (Optical character recognition) para interpretar o texto, exatamente da mesma forma que os scanners trabalham para reconhecer páginas escaneadas, e assim copiá-lo.

Esta solução se chama CAPTCHA (Completely Automated Public Turing Test To Tell Computers and Humans Apart), e busca proteger sites contra robôs através da geração e avaliação de testes que humanos conseguem realizar, mas softwares atuais não.

E CAPTCHA tem sido adotada em massa. É comum de se encontrar, ao criar uma conta em algum site, um campo com letras distorcidas a serem copiadas, para verificar a autenticidade humana do usuário. Observemos que esta parece ser uma solução eficaz contra robôs.

CAPTCHA é indicado especialmente para:

  • Prever SPAM em comentários de blogs.
  • Proteger registros em sites.
  • Proteger endereços de email de spammers.
  • Enquetes on-line.
  • Prever ataques dicionário.
  • Robôs de mecanismos de busca.
  • Worms e SPAM.

Pois bem, temos uma solução. E agora você se pergunta: como embutir esta funcionalidade em meu site?

Felizmente, há diversas implementações de CAPTCHA disponíveis. Apresentarei a implementação mais popular de hoje em dia, o Projeto reCAPTCHA.

 

3. O Projeto reCAPTCHA

O Projeto reCAPTCHA é uma implementação free, segura e disponível para WordPress, MediaWiki, Java, PHP, ASP.NET, Perl, Python e diversos outros ambientes. reCAPTCHA também realiza testes de áudio como uma alternativa a usuários deficientes de visão.

Além disso, reCAPTCHA ajuda a digitalizar livros de Internet Archive e edições antigas do New York Times. A cada teste realizado com sucesso, mais uma palavra  é digitalizada.

Este processo de digitalização ocorre da seguinte forma: cada palavra que não consegue ser reconhecida corretamente por OCR é enviada aos usuários de sites com reCAPTCHA em forma de um teste, junto com outra palavra já conhecida. O usuário deverá interpretar ambas palavras e enviar sua resposta. O sistema aprovará a resposta do usuário se ele digitou corretamente a palavra já conhecida. Aí, o sistema considerará a resposta dada à palavra desconhecida como uma candidata a ser o reconhecimento correto da palavra. Agora, o sistema enviará esta mesma palavra desconhecida a outras pessoas para determinar, com maior certeza, se a resposta candidata é realmente a correta.

reCAPTCHA é utilizado por mais de 100 mil sites, incluindo Facebook, Ticketmaster, e Craigslist.

 

4. Como reCAPTCHA funciona

O seguinte diagrama ilustra o funcionamento de reCAPTCHA.

recaptcha-api-diagram

  1. O usuário carrega a página web com o código JavaScript para o teste reCAPTCHA embutido.
  2. O browser requisita um teste ao servidor de API reCAPTCHA. O servidor fornece ao usuário um teste e um token que identificará o teste.
  3. O usuário preenche o formulário na página web, e envia o resultado a seu servidor de aplicações, juntamente com o token do teste.
  4. O servidor de aplicação envia a resposta do usuário para o servidor de verificação reCAPTCHA. Este a avaliará e retornará para o servidor de aplicações.
  5. Se estiver correto, o servidor de aplicações irá permitir ao usuário acessar algum serviço ou informação. Se não estiver, poderá permitir ao usuário tentar novamente.

 

5. Como implementar reCAPTCHA em meu site

Para implementar reCAPTCHA, siga este processo:

  1. Registre-se em reCAPTCHA Administration Portal para obter um par de chaves pública/privada para cada domínio ou todos os domínios.
    Mais informações em http://recaptcha.net/apidocs/captcha/
      
  2. Insira o código client-side na página desejada. Aqui será inserida sua chave pública. Você pode utilizar a API padrão (utiliza JavaScript), a API não-JavaScript e a API AJAX. Existem diversas opções para a customização do Look & Feel e comportamento.
    Mais informações em http://recaptcha.net/apidocs/captcha/client.html
     
  3. Escreva o código server-side que receberá a requisição, verificará o teste e retornará. Aqui será inserida sua chave privada. 
    Em Java: http://wheelersoftware.com/articles/recaptcha-java.html

 

6. Observações

Implementei reCAPTCHA no projeto Favorpals e até agora tem sido bem satisfatório. Quem tiver algum comentário a fazer, ou experiência com reCAPTCHA ou outra implementação CAPTCHA, está convidado a comentar este post.

Inté =)

Acho que a maioria dos programadores Java, assim como eu, sempre que vão aderir a uma nova tecnologia, procuram abstrações similares às quais temos no mundo Java. 

Pois bem, me vi na necessidade de parametrizar um código escrito em Ruby. A primeira coisa que me veio na cabeça: ARQUIVO PROPERTIES! Seria maravilhoso se existisse em Ruby um conceito exatamente igual, de forma que não fosse necessário gastar muito tempo com isso.

Foi aí que me deparei com o fato de que Ruby não tem um parser para arquivo properties! Pior que isso, nenhuma outra linguagem é obrigada a ter parser para arquivo properties, pois este tipo de arquivos foi criado no mundo Java! É claro que não é difícil de construir um no Ruby (mostrei uma implementação disso no post anterior 10 boas práticas em Ruby WATIR, na Boa Prática 9 - Recuperar mensagens de arquivos de texto), mas seria ideal se houvesse algum recurso similar nativo do Ruby…

Então passei a conhecer YAML. YAML é mais uma daquelas siglas recursivas, e significa YAML Ain’t Markup Language. É uma especificação de um padrão de serialização de dados que seja amigável a humanos, e compatível com todas as linguagens de programação. Seu site oficial é http://www.yaml.org/.

É um projeto muito bacana e simplório, tendo como objetivo fugir da complexidade das linguagens de marcação. Além disso, apresenta parsers para C/C++, Java, Python, Ruby, Perl, C#/.NET, PHP, Javascript, Actionscript e outros.

E o melhor: em Ruby é incrivelmente fácil de se trabalhar com YAML! Veja só:

a) Para carregar uma parametrização de um arquivo YAML em um dictionary:

require "yaml"

# (...)

  # This will load options.yml into config dictionary
  options_file = 'options.yml'
  config = YAML.load_file(options_file)

  # Now it has been loaded. This will get param1 parameter
  # into param1 variable.
  param1 = config['param1']

b) Para serializar dados de um dictionary e gravar em um arquivo YAML:

require "yaml"

# (...)

  config = {
    'param1' => 'value1',
    'param2' => 'value2',
    'param3' => 'value3'
  }

  File.open('options.yml', 'w') do |out|
    YAML.dump(config, out)
  end

Além disso, YAML pode também serializar dados de objetos, arrays, etc. E como possui parsers em diversas linguagens, agora ficou muito mais fácil econômico para realizar serialização de dados entre sistemas construídos em linguagens diferentes, contrapondo o XML! YAML não causará o overhead causado por arquivos XML.

É claro que YAML não tem todo o potencial de XML, pois é apenas um formato de serialização de dados. Outro contraponto é que YAML não possui parsers mais complexos como os parsers de XML em Java e .NET. Mas para muitos propósitos, YAML pode ser mais adequado que XML.

Hoje irei enfatizar a minha solução para escrever código que manipule pop-ups javascript no Ruby WATIR, observando a discrepância que existe na programação para IE e para Firefox.

Fato: muitas aplicações que realizam processamentos server-side costumam apresentar seus resultados por via de pop-ups javascript (isto é muito comum quando utiliza-se AJAX).

O grande problema que ocorre é que, devido ao modo pelo qual WATIR se comunica com cada browser (IE usa a API do Windows e Firefox usa o plugin JSSH), as maneiras que cada uma destas soluções foram desenvolvidas têm naturezas distintas.

No post anterior 10 boas práticas em Ruby WATIR, a Boa Prática 5 - Isolar a discrepância entre IE e Firefox em métodos polimórficos usando o design pattern Strategy apresenta uma solução para este problema, utilizando um padrão de projeto GoF para favorecer isolamento de código e promover reutilização. Não vou repetir o post aqui, portanto verifique neste post a solução apresentada.

Observe que, ainda assim, mensagens pop-up javascript contendo texto são uma fonte de problemas quando se deseja compatibilidade plena entre IE e Firefox. Isto se dá pois cada browser manipula de forma diferente seus pop-ups, e ainda assim em momentos diferentes!

No post anterior 10 boas práticas em Ruby WATIR, a Boa Prática 7 - Evitar ter de manipular mensagens pop-up javascript enfatiza conseqüências e alternativas para isso.

Vila Velha e Vitória são as mais importantes cidades do Espírito Santo. Lugar lindo e agradável. Muito azul. Muito alegre. Modernidade e história vivendo lado a lado.

Andar lá é uma delícia. Pontes, vistas, portos, árvores e pedras. A sinalização do trânsito é um pouco confusa, mas nada que um capixaba não possa nos indicar. 

Um lugar bacana que deixa saudades.

Resumo

Eu tenho apenas 5 meses de experiência com WATIR, mas já me convenci de certas práticas de utilização que considero boas, e que de alguma forma estão trazendo resultados positivos.

Vale lembrar que são todas baseadas no bom-senso.

 


Conteúdo

  1. Utilizar sempre a interface Watir::Browser
  2. Buscar componentes HTML por id sempre que possível
  3. Extrair dados de entrada/saída e mensagens em constantes
  4. Quebrar a lógica em métodos privados para promover reutilização
  5. Isolar a discrepância entre IE e Firefox em métodos polimórficos usando o design pattern Strategy
  6. Escrever no console mensagens de controle e andamento
  7. Evitar ter de manipular mensagens pop-up javascript
  8. Cuidado com inclusões de páginas
  9. Recuperar mensagens de arquivos de texto
  10. Ao testar inclusões com sucesso, realizar a limpeza depois

 


1. Utilizar sempre a interface Watir::Browser

A partir da versão 1.6.2, foi introduzida a interface Watir::Browser, que veio para permitir o tratamento do browser genericamente, sem fazer referência a um browser específico como antigamente, quando eram instanciados objetos Watir::IE ou FireWatir::Firefox.

É boa prática sempre instanciar objetos Watir::Browser, e deixar a definição de qual browser será utilizado fora de seu código-fonte, ou seja, esta decisão não deve ser hard-coded. Assim, pode-se alterar o browser a ser utilizado sem alterar o código-fonte, alavancando desta forma processos automáticos de execução de testes (como scripts ANT) alterarem o browser a ser testado em tempo de execução.

 

2. Buscar componentes HTML por id sempre que possível

Existem várias maneiras de se localizar um componente HTML em uma página, como se pode ver no link Methods Supported by Element. Cada componente tem vários critérios de localização como id, name, value, text, href, index, xpath, class

No entanto, cada um destes critérios tem um motivo de estar sendo usado – e este motivo pode precisar de flexibilidade.

Por exemplo, o critério name representa o nome deste componente em um formulário, e isto comumente é utilizado para refletir em propriedades de form beans do lado do servidor. Ou seja, pode ser um reflexo de uma lógica de negócio – que PODE MUDAR FACILMENTE!

Outros exemplos:

  • o critério href representa uma URL – que PODE MUDAR FACILMENTE!
  • o critério text representa texto de página – que PODE MUDAR FACILMENTE!
  • o critério value representa o valor padrão de um componente – que PODE MUDAR FACILMENTE!

Todavia, o id não tem motivos para mudar facilmente assim, pois seu propósito é geralmente apenas identificar um componente HTML, o que teoricamente não sofre implicações da lógica de negócio. Portanto, é vantajoso localizar programaticamente por id, sempre que possível.

Se o componente desejado não houver atributo id, criá-lo não causará impacto algum na página (ENTRETANTO, verifique se o id desejado já não foi utilizado em sua página ou em alguma página incluída dela). Recomendação: escolha o id longo, claro e baseado no propósito do componente, de forma a não sobrarem motivos para dar este mesmo id a outro componente.

Ao mesmo tempo, essa prática de utilizar id em todos os componentes promove outro aspecto: melhora a legibilidade do código HTML, identificando cada componente quanto a seu propósito.

 

3. Extrair dados de entrada/saída e mensagens em constantes

Dados de entrada e saída de um teste geralmente não mudam durante sua execução. Nem mensagens  esperadas de sucesso ou falha.

Desta forma, é uma boa prática extraí-los para constantes globais e colocar todos no início do código. Assim ganhamos performance, legibilidade e reuso.

Constantes em Ruby são definidas simplesmente como variáveis que iniciam com letra maiúscula. Aqui no exemplo também começam com $ pois são globais.

Antes:

-------------------- test.rb
# requires
require "test/unit"
require "watir"
require "watir/testcase"

##
# Test case for sign up scenarios
# @author Tiago Romero Garcia
#
class Test < Watir::TestCase

  # Method for setting up a test method
  def setup
    @browser = Watir::Browser.start "http://localhost/myapp/index.jsp"
    @browser.maximize
  end

  # (...)

  # Method for testing sign up
  def test_sign_up

    # (...)       

    # tries to sign up
    @browser.text_field(:id, "emailId").set("test@provider.com")
    @browser.text_field(:id, "passwordId").set("testing")
    @browser.text_field(:id, "confirmPasswordId").set("testing")
    @browser.button(:name, "register").click

    # (...)

    # checks if sign up was successful
    test_condition = @browser.text.include?("Thank you for signing up")
    fail_message = "It should show this message:\n" +
                   "- Thank you for signing up"
    assert(test_condition, fail_message)

    # (...)

  end

  # (...) 

end
-------------------- end of test.rb

Depois:

-------------------- test.rb
# requires
require "test/unit"
require "watir"
require "watir/testcase"

# set variables
$Index_page = "http://localhost/myapp/index.jsp"
$New_email = "test@provider.com"
$New_password = "testing"

# messages
$Success_message = "Thank you for signing up"

# (...)

##
# Test case for sign up scenarios
# @author Tiago Romero Garcia
#
class Test < Watir::TestCase

  # Method for setting up a test method
  def setup
    @browser = Watir::Browser.start $Index_page
    @browser.maximize
  end

  # (...)

  # Method for testing sign up
  def test_sign_up

    # (...)

    # tries to sign up
    @browser.text_field(:id, "emailId").set($New_email)
    @browser.text_field(:id, "passwordId").set($New_password)
    @browser.text_field(:id, "confirmPasswordId").set($New_password)
    @browser.button(:name, "register").click

    # (...)

    # checks if sign up was successful
    test_condition = @browser.text.include?($Success_message)
    fail_message = "It should show this message:\n" +
                   "- " + $Success_message
    assert(test_condition, fail_message)

    # (...)

  end

  # (...)

end
-------------------- end of test.rb

 

4. Quebrar a lógica em métodos privados para promover reutilização

Esta prática é bem conhecida e utilizada em outras plataformas, e aqui não tem nada de novo: é boa prática que o desenvolvedor identifique blocos de código repetitivos, e extraia tais blocos em métodos privados que serão chamados por outros.

Blocos de código repetitivos comuns em scripts WATIR: ação de login, ação de logoff, ação de clicar em um botão ou link e testar se houve sucesso, ação de preencher um determinado formulário e testar se houve sucesso, ação de aceitar a falta de certificado quando estiver usando HTTPS, etc.

Mesmo que inicialmente não haja outros métodos chamando um bloco de código potencialmente repetitivo, ainda pode ser bom extrair para um método por motivos de legibilidade e mantenibilidade do código.

 

5. Isolar a discrepância entre IE e Firefox em métodos polimórficos usando o design pattern Strategy

A versão 1.6.2 introduziu um grau de proximidade nunca antes alcançado entre o código feito para o IE e Firefox. Entretanto, muitos detalhes ainda são dependentes de browser, principalmente devido ao modo pelo qual WATIR se comunica com cada browser (IE usa a API do Windows e Firefox usa o plugin JSSH).

Desta forma, para determinadas operações, teremos que construir código específico de browser, como manipulação de pop-ups e DOM.

Mesmo assim, é possível evitar de, para um mesmo teste, construir um test case para IE e outro para Firefox. A solução é simples: aplicação do design pattern Strategy (Estratégia).

Para isto, basta fazer o seguinte:

  1. Definir a Estratégia: Definir um método polimórfico que invocará a operação específica de browser, que será invocado na classe do test case. Lembre-se que em Ruby não existem interfaces como em Java, portanto para isto funcionar, basta escrever da mesma forma este método polimórfico nas classes que implementarem a Estratégia.
  2. Escrever as Estratégias: Criar as classes que irão conter o código específico de browser, dentro do método polimórfico anteriormente definido (implementação da Estratégia).
  3. Usar a Estratégia: Agora, a classe do test case deverá construir ou receber um objeto de uma classe que implementou a Estragégia. Com este objeto, invocar o método polimórfico ao invés dos trechos de código específico de browser.

Exemplo:

1. Definir a Estratégia: é necessário um método para clicar em um botão que exibirá um popup em javascript, e checar se este popup possui uma determinada mensagem. Nosso método será:

clickAndCheckPopup(message)

2. Escrever as Estratégias: escreveremos 2 classes para manipular código específico, uma para IE e outra para Firefox:

-------------------- ie_handler.rb
require "Win32API"
require 'dl/import'
require 'dl/struct'
require "watir/win32"
require "watir/contrib/enabled_popup"

##
# Handler for IE-specific code (strategy design pattern).
# Its goal is to promove independency to test cases.
# They shall not know particularities from browsers.
#
# @author Tiago Romero Garcia
#
class IEHandler

private

  # Method for investigating a popup
  def popup_checker(button_text, message, waitTime=9)
    # get a handle if one exists
    hwnd = @browser.enabled_popup(waitTime)
    if (hwnd)  # yes there is a popup
      w = WinClicker.new

      # I put this in to see the text being input it is not necessary
      # to work "OK" or whatever the name on the button is
      test_condition = w.getStaticText_hWnd(hwnd).include?(message)
      w.clickWindowsButton_hwnd( hwnd, "#{button_text}" )

      # this is just cleanup
      w=nil    

      return test_condition
    end
  end

public

  def initialize(browser)
    @browser = browser
  end

  def clickAndCheckPopup(message)
    @browser.button(:id, "myButtonId").click_no_wait
    puts "Clicked on my button"

    popup_checker("OK", message, 7)
  end    

end
-------------------- end of ie_handler.rb

-------------------- firefox_handler.rb
require "test/unit"

##
# Handler for Firefox-specific code (strategy design pattern).
# Its goal is to promove independency to test cases.
# They shall not know particularities from browsers.
#
# @author Tiago Romero Garcia
#
class FirefoxHandler

public

  def initialize(browser)
    @browser = browser
  end

  def clickAndCheckPopup(message)
    @browser.startClicker("OK")
    @browser.button(:id, "myButtonId").click
    puts "Clicked on my button"

    popupText = @browser.get_popup_text
    popupText.include?(message)
  end    

end
-------------------- end of firefox_handler.rb

3. Usar a Estratégia: a classe do test case ficará assim:

# requires
require "test/unit"
require "watir"
require "watir/testcase"
require "tests/extras/ie_handler"
require "tests/extras/firefox_handler"

# set variables
$Index_page = "http://localhost/jsp/index.jsp"

$Success_message= "Sucessfully operation!"

##
# My test case
# @author Tiago Romero Garcia
#
class MyTest < Watir::TestCase

# Define private methods
private    

  # Method for setting up a test method
  def setup
    puts "------------------------------------------------------------"
    @browser = Watir::Browser.start $Index_page
    @browser.maximize

    if @browser.kind_of?Watir::IE
      @browser_handler = IEHandler.new @browser
    else
      @browser_handler = FirefoxHandler.new @browser
    end
  end

  # Method for tearing down a test method
  def teardown
    @browser.close
  end

  # (...)  

# Define public methods
public      

  # Method for testing my stuff
  def test_my_stuff
    puts "TEST METHOD BEGIN - My stuff"

    # (...)

    test_condition = @browser_handler.clickAndCheckPopup $Success_msg
    fail_message = "It should show this string:" +
                   "\n-" + $Success_msg
    assert(test_condition, fail_message)    

    # (...)

    puts "TEST METHOD END WITH SUCCESS - My stuff"
  end

  # (...)

end

 

6. Escrever no console mensagens de controle e andamento

Geralmente, para todo processo em lote é interessante exibir mensagens de controle e andamento (log). As principais vantagens dessa prática são:

  • Em caso de erro, saber qual o ponto da execução que originou o problema.
  • Ter mais detalhes ao acompanhar a execução do processo “ao vivo”.
  • Documentar todos os passos do processo.

Portanto, é boa prática escrever no console mensagens de texto detalhando o que acabou de ser feito. Isto é especialmente útil após ações como: carregar uma página, clicar em botão ou link, enviar formulário, escolher dentre uma lista de seleção, etc.


7. Evitar ter de manipular mensagens pop-up javascript

Mensagens pop-up javascript contendo texto são uma fonte de problemas, quando se deseja compatibilidade plena entre IE e Firefox. Isto se dá pois cada browser manipula de forma diferente seus pop-ups, e ainda assim em momentos diferentes!

Por exemplo, na boa prática n° 5, o código para IE dentro de ie_handler.rb executa um programa externo ao browser que será um listener de pop-ups javascript. Possui uma vantagem: consegue capturar pop-ups mesmo que o browser mude de página.

Já o trecho para Firefox dentro de firefox_handler.rb realiza o listening dentro do código. Este não consegue capturar pop-ups se o browser mudar de página.

A boa prática aqui é evitar o máximo possível de se usar este critério para avaliar os resultados de um teste. Em caso de ser uma mensagem revelando o resultado de algum processamento, verifique se após este processamento a página sofrerá alguma alteração. Se sofrer, ao invés de avaliar a mensagem do pop-up, pode-se avaliar se a página sofreu tal alteração.


8. Cuidado com inclusões de páginas

Sites programados em linguagens de scripting server-side como JSP e PHP permitem a inclusão de páginas em outras. Esta é uma prática muito utilizada, especialmente para a reutilização de trechos estáticos do site como menu, cabeçalho e rodapé.

Entretanto, isto pode bagunçar demais a cabeça do programador WATIR, pois pode ser que alguma página incluída tenha tags com as mesmas características que tags na página que as inclui. Assim, ao procurar por tags na página, pode ser que WATIR encontre primeiro tags das páginas incluídas! 

As boas práticas para evitar este problema são:

  • Quando lidar com páginas que incluam outras páginas, verifique o HTML gerado no browser se a tag que você quer achar será realmente identificada pelo critério que você escolheu. Exiba o código-fonte do browser e faça uma busca no texto para ver se não encontra outras tags com o mesmo critério.
  • Escreva tags com ids realmente “identificadores”, ou seja, que descrevam realmente o propósito da tag, mesmo que seja um nome longo. Por exemplo, ao invés de fazer
    <input type="text" id="name">

    prefira fazer

    <input type="text" id="signupFormName">

    Assim, você reduz a chance de acontecerem problemas assim.

 

9. Recuperar mensagens de arquivos de texto

Aplicações web podem recuperar mensagens de arquivos de texto ou arquivos properties, principalmente quando utiliza-se internacionalização (i18n). Quando for este caso, é uma boa prática recuperar as mensagens diretamente destes arquivos, e não escrevê-las novamente na classe de teste. Quanto menos hard-coded puderem ser as mensagens, melhor!

Eu utilizo para isto a seguinte classe leitora de arquivos properties de java, disponível neste link. Agradeço a Devender Gollapally por permitir a reprodução e utilização de seu código aqui.

-------------------- java_props.rb
#
# Class for handling java-style properties files
# Author: Devender Gollapally
# From: http://devender.wordpress.com/2006/05/01/reading-and-writing-
java-property-files-with-ruby/
#
class JavaProps
  attr_reader :file, :properties

  #Takes a file and loads the properties in that file
  def initialize file
    @file = file
    @properties = {}
    IO.foreach(file) do |line|
      @properties[$1.strip] = $2
          if line =~ /([^=]*)=(.*)\/\/(.*)/ || line =~ /([^=]*)=(.*)/
    end
  end

  #Helpfull to string
  def to_s
    output = "File Name #{@file} \n"
    @properties.each {|key,value| output += " #{key}= #{value} \n" }
    output
  end

  #Write a property
  def write_property (key,value)
    @properties[key] = value
  end

  #Save the properties back to file
  def save
    file = File.new(@file,"w+")
    @properties.each {|key,value| file.puts " #{key}= #{value} \n" }
  end

end
-------------------- end of java_props.rb

 

10. Ao testar inclusões com sucesso, realizar a limpeza depois

Quando é necessário testar uma página de inclusão, o fluxo principal deve ser testado, ou seja, uma inclusão deve ser feita com sucesso. Geralmente são utilizados dados de teste para isto.

Isto tem uma contrapartida: os dados ficam armazenados no banco de dados, e isto representa lixo. Quanto mais vezes o teste for executado, mais lixo será gerado e assim vai…

O ideal é que seja realizada uma limpeza imediatamente após o sucesso do teste. Deve ser projetada uma maneira segura para isto, que não coloque a integridade dos dados em risco.

Uma possibilidade para isto é construir um processo de negócio para essa limpeza, e disponibilizar uma URL que delegue a chamada a este processo de negócio. Em uma arquitetura Java EE, o processo de negócio pode ser um método de um EJB, ou um serviço de um BusinessObject; e a URL pode chamar um servlet que invoque tal EJB ou BusinessObject.

Sugiro as seguintes etapas:

  • Passagem por parâmetro da URL do identificador do dado recém-incluído
  • Autenticação do usuário de teste (ou do cliente que estiver chamando a URL)
  • Deleção do dado recém-incluído baseado no identificador
Formado

Formado

Olá! Antes de mais nada, feliz ano novo!

Fiquei 1 mês sem postar adequadamente devido a diversas ocupações que tive: SCWCD, Curso de EJB na SynOS, viagens de fim de ano e FORMATURA!

Finalmente, semana passada me formei Bacharel em Ciência da Computação! Nem é preciso dizer o tanto que teve bom. Depois de 5 anos ralando, cá estou bacharel.

Agora de volta a rotina… Vou retomar alguns posts sobre Ruby WATIR e outras features que ando trabalhando. E também enviarei fotos de alguns lugares que visitei recentemente. :)

Quem quiser ver fotos da formatura:

Inté!

Após 2 meses encarando o mesmo problema e bebendo muito café, finalmente consegui solucioná-lo, depois de muito desgaste e tentativas em vão, e vou compartilhar o conhecimento convosco!

É o seguinte: trabalho em um projeto web e desenvolvo test cases em Ruby WATIR para a automação de testes de regressão.

E eis que fui incumbido de desenvolver o test case para uma determinada página de cadastro. Entretanto, tal página utiliza um componente de texto do tipo rich-text, que oferece certas funcionalidades de edição de texto, como formatação de fonte, parágrafo, cores de fundo, etc. Utilizamos uma biblioteca free para isto: Whizzywig.

Este componente é utilizado na nossa view como um componente HTML TEXTAREA, mas depois é executado um método em javascript que o transforma em um IFRAME criando uma nova sub-página com todas essas funcionalidades, da seguinte maneira:

<textarea id="descriptionId" name="description">
   (...)
</textarea>
<script type="text/javascript">
   (...)
   makeWhizzyWig("descriptionId", "bold italic underline |
        left center right | number bullet indent outdent |
        undo redo | color hilite rule");
</script>

Nestas condições, para fazer o WATIR preencher o conteúdo deste componente, não é possível utilizar o seguinte código (considerando @browser como a instância de Watir::Browser):

@browser.text_field(:id, "descriptionId")

Isto ocorre pois componentes rich-text têm a responsabilidade de escrever em HTML (para dar suporte à tais formatações), e para isto eles utilizam um IFRAME, e NÃO É POSSÍVEL PERMITIR AO USUÁRIO ESCREVER DIRETAMENTE NUM IFRAME. A mágica que ele faz para isto é a seguinte: é configurado um listener de teclado no IFRAME, e a cada tecla pressionada, ele programaticamente “digita” esta tecla no IFRAME, ou seja, ele escreve em seu conteúdo utilizando o DOM de sua página. O componente engana tão bem, que até tem o cursor de texto piscando à medida que o usuário “digita”.

Isto pode ser lindo, mas impossibilita nosso querido WATIR a setar o conteúdo deste campo TEXTAREA!

A solução que encontrei é navegar programaticamente até o conteúdo do IFRAME e alterar diretamente lá.

No Internet Explorer, eu naveguei no conteúdo HTML gerado (através do plugin IE Developer Toolbar) e consegui fazer isto sem muito esforço:

@browser.
      frame(:id, "whizzydescriptionId").
      locate_tagged_element("body", :id, "descriptionId").
      innerText = $Description

Isto é possível pois o Internet Explorer é acessado pelo WATIR através da API do Windows.

cafeAgora, no Firefox, a história já é completamente diferente! Pois o mesmo é acessado pelo WATIR através de um plugin chamado JSSH. Esse plugin abre uma sessão em um terminal, e responde a comandos próprios (que permitem mais ou menos utilizar javascript). O FireWatir trabalha enviando comandos via socket ao plugin JSSH do Firefox, que se encarrega de realizar o trabalho sujo com o browser.

Primeiramente, eu tentei utilizar todas as formas possíveis de javascripts para alterar o DOM da página em exibição, mas isto não alterava a página em tempo de execução, mas sim em uma nova página gerada.

Depois de muito quebrar a cabeça, mergulhei na API do JSSH (que tem documentação fraca), abri um terminal e comecei a tentar de tudo, comando por comando, até que uma hora cheguei à solução! E consegui alterar o conteúdo do IFRAME em tempo de execução, manipulando seu DOM!

O código que utilizei foi o seguinte:

@browser.
      jssh_socket.send(
          "#{FireWatir::Firefox::DOCUMENT_VAR}.
          getElementsByTagName('iframe')[0].contentDocument.
          body.innerHTML = '" + $Description + "';\n", 0)
@browser.read_socket()

O que ele faz é o  seguinte: envia um comando para o JSSH via socket, e depois lê a resposta socket (este comando não tem resposta, mas esta chamada é necessária, é como se fosse um flush).

Para localizar o objeto referente ao browser em execução, utilizamos a constante #{FireWatir::Firefox::DOCUMENT_VAR}. A partir daí entramos na API do JSSH, e navegando por vários atributos, chegamos a poder setar o conteúdo do IFRAME. Ufa!

Espero que isto seja útil a alguém! :)

Como eu havia dito no post anterior Livro Head First Servlets & JSP, eu passei 1 mês me preparando para realizar a prova de certificação Sun Certified Web Component Developer for the Java 2 Platform, Enterprise Edition (SCWCD). Eu realizei a prova hoje e felizmente fui aprovado com 81%! :)

E vou contar nesse post como que foi.

SCWCD

Em primeiro lugar, eu já venho trabalhando com servlets e JSPs desde junho. E já tive experiência anterior com servlets. Isto me ajudou bastante para fixar o conteúdo.

Apesar de ter feito a versão mais nova dessa certificação, referente à versão 5 do JEE, eu li o Head First Servlets referente à versão 1.4 e consegui fazer a prova numa boa! Segundo Bryan Basham, co-autor da prova e também do livro mencionado, as diferenças entre as 2 últimas versões são as seguintes:

  1. Não há diferenças nos objetivos do exame entre as versões.
  2. O novo exame (SCWCD 5) é ainda baseado na versão J2EE 1.4 (com Servlet 2.5 e JSP 2.0). Os novos tópicos adicionados ou atualizados são: JSTL tags, dynamic attributes e um pouco mais de SingleThreadModel.
  3. O exame SCWCD 5 não possui questões relacionadas a JSF, mas SCWCD 6 possuirá.
  4. O exame SCWCD 5 tem questões baseadas em performance, diferentemente do teórico exame SCWCD 1.4.

Após ler o livro (e reler pontos que não tinham ficado muito claros), eu li bastante um ótimo resumo publicado por Daniel Martins em um artigo em seu blog, disponível nesta página.

Depois disso, tirei o restante do tempo antes da prova em fazer vários simulados (os famosos mocks). Nos seguintes links há vários mocks bacanas, e melhor, FOR FREE (evite mocks do SCWCD da versão J2EE 1.3, eles podem confundir a cabeça):

No dia anterior, eu preferi não ocupar muito a minha cabeça e fiz apenas o coffee cream mock do final do livro Head First.

E é isso aí. Ano que vem vamos ver se eu começo a estudar pras provas de SCBCD e SCEA :)

Head First Servlets & JSPFaz tempo que não posto, é porque estou me preparando para realizar a certificação SCWCD (Sun Certified Web Component Developer) e estou lendo este ótimo livro: Head First Servlets & JSP. Os autores do livro são co-autores da prova de certificação.

O livro cobre todo o conteúdo da prova de uma maneira bem-humorada  e não-cansativa.

Confesso que virei fã da série Head First. A maneira que os conceitos são apresentados é muito interessante, o livro utiliza de recursos como personagens, diálogos, cenários da vida real, lembretes e outras coisas doidas e até mesmo idiotas que fazem o gosto de quem estuda computação.

Realmente atingem o efeito de fixar o conceito e também fazem muitas divagações acerca do que é ensinado. Também tem muitos exercícios práticos e é necessário exercitar muito a imaginação.

Uma dica: como qualquer livro de TI e computação, prefira sempre ler em inglês, pois traduções quase nunca ficam 100%, e além disso você já se acostuma com os termos utilizados na prova de certificação.

Link para leitura on-line completa do livro: http://books.google.com.br/books?id=WoA3F0UQfvQC

Link para a compra do livro na Amazon: http://www.amazon.com/Head-First-Servlets-JSP-Certified/dp/0596005407

Próxima Página »