logo

Dicas avançadas de ruby (capybara/cucumber)

Esse post foi escrito originalmente em inglês (http://lazytester.com/lazy-tips-for-your-capybaracucumber-tests/) e achei que seria bom ter a versão na minha lingua nativa :)

Como de constume, continuo vendo um pessoal dizendo que automação não é desenvolvimento, logo não usam técnicas como DRY (dont repeat yourself) ou coisas bacanas da linguaguem que escolheram usar para automatizar seus testes.

Para ajudar essas pobres almas, eu juntei mais algumas dicas avançadas para que você deixe seu código mais bonito, semântico e o tempo que você vai economizar para estender seus testes, você poderá gastar ficando mais tempo no break do café :)

1 - Você deveria usar o Bundler

Uma das coisas legais do ruby é seu gerenciador de depenência chamado bundler.
Com ele você tem o controle de quais gems o seu projeto de teste depende.

gem install bundler  
//(*sudo* se necessário..)

No diretório raiz do seu projeto, crie um arquivo chamado Gemfile (sem extensão) . Uma cópia do conteúdo pode ser vista abaixo (adicione outras gems que você usa em seus scripts)

source 'https://rubygems.org'  
gem 'selenium-webdriver'  
gem 'capybara'  
gem 'cucumber'  

salve o arquivo e chame o comando abaixo no mesmo diretório.

bundler install  

O bundler vai pegar a última versão de todas as gem's do arquivo Gemfile e irá instalar para você. Caso ela já esteja instalada, vai mostrar na tela a seguinte mensagem: "Using gem XXXX".

Se você quer uma versão específica de uma gem, pode usar a sintaxe ao lado

gem 'rails', '3.0.0.beta3'  

Quer saber mais sobre o bundler? http://bundler.io/ .

2 - Se possível, evite usar IF's

O condicional if é o método mais comum utilizado em todas as linguagens e é facil de usar, porém com o tempo e o aumento de complexidade do seu script, fica fácil se perder em tantos if's jogados por ai.

Muitos ifs tornam o código difícil de manter, ler e estender.

Se você tem uma condicional simples, não tem problema usar if mas se ela é um pouco maior, tente usar case.

Veja o exemplo abaixo:

    If a==b
        puts "true!"
    elsif a > b
        puts "a is bigger!"
    elsif b > a
        puts "b is bigger!"
    elsif c > a
        puts "forgot about c... c is bigger!"
    else
        puts "omg something is wrong.."
    end

Usando case :

    case 
        when a > b then puts "true"
        when b > a then puts "a is bigger"
        when c > a then puts "forgot about c... c is bigger!"
        else puts "omg something is wrong"
    end

Não é nem pela quantidade de linhas, mas de como o código é exibido. Vamos concordar que é bem mais semântico e fácil de entender

3- Não declare variáveis vazias( x = nill)..

Muitas pessoas que vieram do mundo JAVA, constumam declarar variáveis vazias para poder popular mais tarde dentro de um loop ou dentro de uma condicionais (if ou case)
.

Pelo amor de deus não faça mais isso..

Se você está tentando preencher uma variável dentro de uma condicional, tente usar a sintaxe abaixo:

if:

    myVariable = if a == b
        "true!"
    else
        "false!"
    end

case:

    myVariable = case a
        when b then "true!"
        when c then "omg its equal c!"
    end

Reparou que eu não faço myVariable = "true"? Pois cada condicional tem um valor de retorno implícito e ele preenche a variável com esse valor de retorno.

3.1 Você pode expandir essa dica para métodos também:

    def returnSomething(param)
        if param
            "true"
        else
            "false"
        end
    end

Viu? sem usar o método return ..e funciona tranquilamente :)

3.2 Tente usar inline / if's tenários

    string = if true
        "some text"
    end

É o mesmo que:

    string = "some text" if true

Ou você pode usar a forma tenária de se escrever:

    string = 1==1 ? "some text" : "other text"

4 - Coloque seus processos comuns em métodos de suporte.

Com cucumber você já consegue reutilizar STEP's (isso é nativo da biblioteca e é genial) porém algumas vezes existem processos que não entram nesses steps e você pode utilizar eles frequentemente.

Exemplo: Você precisa fazer logoff da aplicação para testar se o processo de teste trocar senha está funcionando. .

O Step disso poderia ser: "E o usuário loga novamente para confirmar a alteração de senha".

O step não é um processo específico de logoff porém utiliza os passos para fazer um logoff. Então por que não criar um método específico para ele?

Ou exemplo poderia ser um método que faz um calculo que é geralmente utilizado em grande parte dos testes?

Dentro da pastasupport, qualquer arquivo .rb que você colocar lá dentro, os métodos poderão ser acessados de qualquer arquivo de teste dentro do seu projeto

Mais fácil que fritar ovo!

5 - Utilize valores padrão para argumentos dos seus métodos

Em tempos sombrios onde o selenium 1 era beta e eu estava aprendendo a programar em java.
Meus métodos orientados a objetos utilizados para dar suporte aos meus testes tinham 3..5.. 10 argumentos
EU pensava que era normal por causa da complexidade dos testes... o restante da história eu vou deixar de lado para poupar vocês da dor e agonia.

Primeiro de tudo, tente evitar fazer métodos que use mais que 4 argumentos.

Também verifique quais argumentos são passíveis de um valor padrão.

Por exemplo, vamos imaginar que você precise chamar um método para salvar um log no banco de dados e algumas vezes você precisa jogar exibir na tela alguma mensagem no console. Na maior parte do tempo, você não irá exibir nada no console.

Em tempos passados eu faria desta forma:

    def savelog(log_string,console)
        #do something to save the log_string
        ...
        if console
            puts log_string
        end
    end

    savelog ("something was done successfully", false)
    savelog ("something fail!", true)
 

Como falado acima, grande parte do tempo não será exibido nada no console, então por que temos que chamar o método savelog sempre com o argumento "console" false?Por que não deixar esse argumento com falor padrão = false?

Vamos refatorar!

    def savelog(log_string, console=false)
        ...
    end

    savelog ("something was done successfully")
    savelog ("something fail!", true)

Repare que só deixei explícito o argumento console igual a "true" e quando eu não mando nada, ele usa o padrão do método

5.2 Se você precisar mandar mais que 4 argumentos, procure enviar um hash.

Imagine que você tem um método assim:

    def myMethod(arg1,arg2,arg3,arg4,arg5)
        ...
    end

    myMethod(1,2,3,4,5)
    myMethod(1,2,nil,nil,5)

Se você precisa o método, mas não precisa passar todos os argumentos, mesmo com valor padrão você precisaria colocar algo para identificar a ordem certa dos paramêtros. (arg3 e arg4 tem que ser nill para identificar o arg5)

Feio né?
Sabia que podemos chamar um método com argumentos em hash?

    def myMethod(options={})
        puts options[:arg1]
        puts options[:arg2]
        puts options[:arg3]
        puts options[:arg4]
        puts options[:arg5]
    end

    myMethod(:arg1=>1, :arg5 => 5 :arg2=>2, )

Bem mais bonito e semântico! E perceba que não está na ordem de antigamente, pois ele identifica o nome do parametro pela chave do hash!

mas espera um pouco... o arg3 e arg4 vai exibir nill? vai dar pau?

Vai exibir "nill" e isso nos leva a última dica :)

5.3 - Tratando variáveis nulas (nill)

Lembra do exemplo anterior?

    def myMethod(options={})
        puts options[:arg1]
        puts options[:arg2]
        puts options[:arg3]
        puts options[:arg4]
        puts options[:arg5]
    end

    myMethod(:arg1=>1, :arg5 => 5 :arg2=>2, )

puts options[:arg3] e puts options[:arg4] vai ser exibido nill. Mas e se eu não quero exibir nill? É possível setar um valor default para caso o argumento não venha OU venha _nill

    def myMethod(options={})
        msg = "não veio o argumento!"
        puts options[:arg1] ||= msg
        puts options[:arg2] ||= msg
        puts options[:arg3] ||= msg
        puts options[:arg4] ||= msg
        puts options[:arg5] ||= msg
    end

Agora caso não venha o parâmetro requisitado, ele irá preencher com uma mensagem genérica "não veio o argumento"!

Por enquanto é só.
Espero que tenham gostado!
Dúvidas, críticas, café, sugestão.. sinta-se a vontade para comentar!


Sobre o Autor: Leonardo Galani é Agile Tester, dev, gamer, dj and etc. Mantém o fórum http://agiletesters.com.br | http://leonardobg.com.br (profile)| http://lazytester.com (blog em inglês)
http://br.linkedin.com/in/leonardogalani/

comments powered by Disqus