domingo, 7 de novembro de 2010

Aprender um pouco de RSpec com o exemplo FizzBuzz


Vamos falar de teste, e de RSpec para ser específico, confesso que ainda estou no comecinho com RSpec mas o que eu já vi e entendi da pra ajudar quem ainda não viu nada, e como exemplificar é melhor que falar, vou fazer um exemplo chamado fizzbuzz que vi no dojo-rio a algumas semanas, naquela ocasião foi feito em Python mas como meu foco é Ruby, vou fazer com ele.

O exercício chamado Fizzbuzz é o seguinte, se um número for divisível por 3, escreve fizz, se o número for divisível por 5, escreve buzz, se for pelos 2, escreve fizzbuzz, se não for por nenhum deles, retorna o próprio
número.

Se você é aquela pessoa anciosa que não quer ver o passo a passo do problema e só quer a resposta, e continuar sem entender nada, vai logo pro final deste post.
 
Abra o terminal e instale a gem do rspec
sudo gem install rspec

Vá para seu diretório de projetos e crie um diretório chamado fizzbuzz e entre nele:
cd project && mkdir fizzbuzz && cd fizzbuzz
// com isso ele fará as 3 requisições uma depois da outra

Crie um arquivo chamado test.rb e salve. Este é o arquivo que vai ser lido quando rodar o spec.
gedit test.rb

Para verificar se o nosso ambiente de teste está funcionando faça:

describe "Teste" do
 it "para ver se está funcionando" do
 
1.should == 1
 end
end

E depois rode no terminal:
clear && spec test.rb --color --format nested
# o comando clear limpa a tela, e depois agente executa o camando do RSpec, ele vai adicionar cor e identar

Linha por linha:
describe "Teste" do # describe => declara o grupo de exemplos, é um bloco tem o (do) para abrir e o (end) para fechar esse bloco

it "para ver se está funcionando" do # it => declara o exemplo

1.should == 1 # should => é um método da classe Oject, se a espectativa retornar true ela passa, se retornar false o should mostra uma mensagem de falha

Agora voltando ao problema, a primeira coisa que você tem que pensar é existe 4 regras pra resolver, então
tem que começar pela que é teoricamente mais fácil, que é:
Se é divisível por 3, retorna fizz.

describe "Fizzbuzz" do
  context "é divisível" do
   
it "por 3" do
      fizzbuzz(9).should == "fizz"
   
end
  end
end

Se você for no terminal e rodar a spec vai acontecer isso:

Fizzbuzz
  é divisível
    por 3 (FAILED - 1)

1)
NoMethodError in 'Fizzbuzz é divisível por 3'
undefined method `fizzbuzz' for #<Spec::Example::ExampleGroup::Subclass_1::Subclass_1:0x7f7f950a5250>
./test.rb:4:

Finished in 0.040514 seconds

1 example, 1 failure

Mas é claro, não existe método fizzbuzz, então ta na hora de criar um outro arquivo, chamado "fizzbuzz" mesmo:
gedit fizzbuzz.rb

e chamar este arquivo no nosso arquivo de teste, o test.rb:
require "fizzbuzz"

e no arquivo fizzbuzz.rb

def fizzbuzz(numero)
  "fizz"
end

Passou, mas ainda não resolveu nossa primeira regra. Para resolver a primeira regra é bem simples.

def fizzbuzz(numero)
  if numero %3 == 0 # Se for divisível por 3, retorna a palavra fizz
    "fizz"
  end
end

Se você for curioso como eu, vai querer ver se realmente isso ta funcionando como o esperado, troca a palavra fizz por buzz:

Fizzbuzz
  é divisível
    por 3 (FAILED - 1)

1)
'Fizzbuzz é divisível por 3' FAILED
expected: "fizz",
     got: "buzz" (using ==)
./test.rb:5:

Finished in 0.036305 seconds

1 example, 1 failure

Bang !!! ... o teste esperava a palavra fizz, porém o método retornou a palavra buzz, volte com a palavra fizz e seguiremos nossas regras. Agora se é divisível por 5, retornar buzz.
Depois do primeiro bloco it adicione este bloco:

it "por 5 " do
  fizzbuzz(10).should == "buzz"
end

Se você rodar o teste vai ver que o primeiro teste passou e o segundo teve o seu retorno nulo, por que ele não é divisível por 3, e como não tem nenhum else ele vai retornar nulo.

Fizzbuzz
  é divisível
    por 3
    por 5  (FAILED - 1)

1)
'Fizzbuzz é divisível por 5 ' FAILED
expected: "buzz",
     got: nil (using ==)
./test.rb:10:

Finished in 0.039466 seconds

2 examples, 1 failure

Para resolver isso coloque a condição para ser divisível por 5.

def fizzbuzz(numero)
  if numero %3 == 0
   
"fizz"
  elsif numero %5 == 0
    "buzz"
  end
end

Agora passou, eu sei que parece chato para alguns, mas estou seguindo um esquema de dojo, então explico em detalhes cada passo, para realmente a pessoa entender como faz.
A próxima regra é para ver se é divisível por 3 e 5 ao mesmo tempo, retornar fizzbuzz.
Depois do bloco do it coloque este aqui.

it "por 3 e 5" do
  fizzbuzz(15).should == "fizzbuzz"
end

E o método vai ficar diferente, por que primeiro temos que testar se é divisível pelos 2 números, para depois testar as outras condições

def fizzbuzz(numero)
  if numero %3 == 0 and numero %5 == 0
    "fizzbuzz"
  elsif numero %3 == 0
    "fizz"
 
elsif numero %5 == 0
    "buzz"
  end
end

Agora para a última regra, verificar se não é divisível por nenhum dos 2 números. Abaixo do contexto coloque isto:

require "fizzbuzz"

describe "Fizzbuzz" do
  context "é divisível" do
    it "por 3" do
     
fizzbuzz(9).should == "fizz"
    end
   
    it "por 5 " do
      fizzbuzz(10).should == "buzz"
    end
   
    it "por 3 e 5" do
      fizzbuzz(15).should == "fizzbuzz"
    end
  end
 
 
context "se não for divisível por 3 ou 5" do
    it "retorna o mesmo valor" do
      numero = 7
      fizzbuzz(numero).should == numero
    end
  end
 
end
 
Fiz assim para ficar mais organizável e legível, agora é só adicionar este último else para finalizar o
processo.

def fizzbuzz(numero)
  if numero %3 == 0 and numero %5 == 0
    "fizzbuzz"
  elsif numero %3 == 0
    "fizz"
  elsif numero %5 == 0
   
"buzz"
  else
    numero
  end
 
end

Essa forma é a que eu vejo ser a mais simples, tem como otimizar mais este código, mas isso fica para outra hora, espero que eu tenha ajudado de alguma forma, até a próxima.
 

Nenhum comentário:

Postar um comentário