Pequena Intro BDD com Cucumber
Posted in Programação, Ruby/Rails, testing on October 22nd, 2009 by Edipo Luis Fderle – Be the first to commentWarning: Missing argument 1 for GeSHi::GeSHi(), called in /home/storage/f/73/ab/bitside1/public_html/edipo_blog/blog/wp-content/plugins/codecolorer/codecolorer-core.php on line 137 and defined in /home/storage/f/73/ab/bitside1/public_html/edipo_blog/blog/wp-content/plugins/deans_code_highlighter/geshi.php on line 432
Warning: Missing argument 2 for GeSHi::GeSHi(), called in /home/storage/f/73/ab/bitside1/public_html/edipo_blog/blog/wp-content/plugins/codecolorer/codecolorer-core.php on line 137 and defined in /home/storage/f/73/ab/bitside1/public_html/edipo_blog/blog/wp-content/plugins/deans_code_highlighter/geshi.php on line 432
Neste post vamos ver um pouco(bem pouco) sobre BDD ou Behavior Driven Development utilizando o Cucumber.
Aqui você ira escrever os testes antes de escrever
Primeiramente vamos começar criando uma “funcionalidade” para descrever qual o comportamente que queremos que nosso codigo tenha, isso é representado através de features, features são arquivos em pleno inglês ou português(sim tem como) para descrever o comportamente de um requesito, por exemplo.
Uma feature no cucumber tem seu esqueleto assim:
Feature: Be Awesome (Fazer alguam coisa, Responder alguma coisa, funcionalidade)
1 2 3 4 5 | Senario: Delete products Given a list with 5 products When I select three items And I click delete Then I should see only 2 products |
Ou no bom Português:
1 2 3 4 | Cenário: Deletar produtos Dada uma lista com 5 produtos Quando eu selecionar três itens E eu clicar em excluir Então eu deveria ver apenas 2 produtos |
Como você pode ver isso é facilmente compreendido por qualquer um, não precisa ser técnico nem nada para saber realmente o que o cenário faz.
Nosso pequeno exemplo:
Vamos criar um exemplo bem simples, nosso código irá ser uma calculadora, essa calculadora irá somente multiplicar(nosso cliente não sabe multiplicar ainda então….
).
Com uma conversa de uns minutos com o cliente juntos criamos uma feature assim:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | Feature: Multiplication Operation In order to to learn math As a math idiot I want multiply two numbers Scenario: Add two numbers I initialize calculator with 10 and 10 When I press go Then I should see 100 Scenario: Add two negative numbers Given I initialize calculator with -2 and -2 When I press go Then I should see 4 |
Vamos começar executando nossa feature, inicialmente somente com o primeiro cenário, crie um arquivo chamado calculadora.feature e coloque o seguinte conteudo:
1 2 3 4 5 6 7 8 9 | Feature: Multiplication Operation In order to to learn math As a math idiot I want multiply two numbers Scenario: Add two numbers I initialize calculator with 10 and 10 When I press go Then I should see 100 |
Agora vá até um terminal e execute:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | cucumber calculadora.feature Feature: Multiplication Operation In order to learn math As a math idiot I want to be told the multiply tow numbers Scenario: Add two numbers # calculadora.feature:6 Given I initialize calculator with 10 and 10 # calculadora.feature:7 When I press go # calculadora.feature:8 Then I should see 100 # calculadora.feature:9 1 scenario (1 undefined) 3 steps (3 undefined) 0m0.002s |
Bom, como vemos, temos 1 cenário que ainda não está definido, também tempos os três passos que também não estão definidos, logo abaixo o cucumber já nos da os passos para implementar, vamos copiar esse código e colocar dentro de um arquivo chamado calculadora_steps.rb, e então executar novamente nossa feature.
Rodando a feature iremos receber algo do tipo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | $ cucumber calculadora.feature Feature: Multiplication Operation In order to learn math As a math idiot I want to be told the multiply tow numbers Scenario: Add two numbers # calculadora.feature:6 Given I initialize calculator with 10 and 10 # calculador_steps.rb:1 TODO (Cucumber::Pending) ./calculador_steps.rb:2:in `/^I initialize calculator with 10 and 10$/' calculadora.feature:7:in `Given I initialize calculator with 10 and 10' When I press go # calculador_steps.rb:5 Then I should see 100 # calculador_steps.rb:9 1 scenario (1 pending) 3 steps (2 skipped, 1 pending) 0m0.003s |
Bom, vimos que ao pegar o código que o cucumber nos deu ele está usando o método pedding em todos os três passos, isso obviamente quer dizer que eles estão pendentes, vamos começar implementando o comportamento do primeiro passo, que é:
1 2 3 | Given /^I initialize calculator with 10 and 10$/ do pending end |
Fazemos isso:
1 2 3 | Given /^I initialize calculator with 10 and 10$/ do @c = Clac.new(x,y) end |
E rodamos nossa feature:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | cucumber calculadora.feature Feature: Multiplication Operation In order to learn math As a math idiot I want to be told the multiply tow numbers Scenario: Add two numbers # calculadora.feature:6 Given I initialize calculator with 10 and 10 # calculador_steps.rb:1 uninitialized constant Calc (NameError) ./calculador_steps.rb:2:in `/^I initialize calculator with 10 and 10$/' calculadora.feature:7:in `Given I initialize calculator with 10 and 10' When I press go # calculador_steps.rb:5 Then I should see 100 # calculador_steps.rb:9 Failing Scenarios: cucumber calculadora.feature:6 # Scenario: Add two numbers 1 scenario (1 failed) 3 steps (1 failed, 2 skipped) 0m0.003s |
OPS, parece que não temos a classe calc, e de fato não temos, vamos cria-la, para isso, nesse caso, iremos escrever a classe no mesmo arquivo que temos nossos passos, isso não é uma boa prática, então não o faça, fiz aqui somente para fins de explicação
.
Então no começo do arquivo calculadora_steps.rb iremos ter:
1 2 3 4 5 6 | class Calc def initialize(x,y) @x =x @y = y end end |
Certamente isso irá fazer com que o teste passe, rodando a feature:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | cucumber calculadora.feature Feature: Multiplication Operation In order to learn math As a math idiot I want to be told the multiply tow numbers Scenario: Add two numbers # calculadora.feature:6 Given I initialize calculator with 10 and 10 # calculador_steps.rb:8 undefined local variable or method `x' for #<Object:0x8df61c> (NameError) ./calculador_steps.rb:9:in `/^I initialize calculator with 10 and 10$/' calculadora.feature:7:in `Given I initialize calculator with 10 and 10' When I press go # calculador_steps.rb:12 Then I should see 100 # calculador_steps.rb:16 Failing Scenarios: cucumber calculadora.feature:6 # Scenario: Add two numbers 1 scenario (1 failed) 3 steps (1 failed, 2 skipped) 0m0.003s |
E como diria Dave Thomas em Agile Web Development With Rails “…”, erro:
Ele está nos dizendo que a variavel x nao esta definida, vamos alterar um pouco nosso passo e no lugar do 10 and 10, vamos colocar um expresao regular, e passar para um bloco, assim:
1 2 3 | Given /^I initialize calculator with (.*) and (.*)$/ do |x,y| @c = Calc.new(x.to_i, y.to_i) end |
Tambem utilizamos o metodo para converter os valores para inteiros, o to_i. E agora sim rodamos novamente nossa features, e esperamos que os testes passem.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | cucumber calculadora.feature Feature: Multiplication Operation In order to learn math As a math idiot I want to be told the multiply tow numbers Scenario: Add two numbers # calculadora.feature:6 Given I initialize calculator with 10 and 10 # calculador_steps.rb:8 When I press go # calculador_steps.rb:12 TODO (Cucumber::Pending) ./calculador_steps.rb:13:in `/^I press go$/' calculadora.feature:8:in `When I press go' Then I should see 100 # calculador_steps.rb:16 1 scenario (1 pending) 3 steps (1 skipped, 1 pending, 1 passed) 0m0.003s |
E Bingo, passou! Vamos para o segundo passo:
1 2 3 | When /^I press go$/ do @result = @c.go end |
Quermos o resultado do metodo go na variavel result, vamos executar a feature:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | cucumber calculadora.feature Feature: Multiplication Operation In order to learn math As a math idiot I want to be told the multiply tow numbers Scenario: Add two numbers # calculadora.feature:6 Given I initialize calculator with 10 and 10 # calculador_steps.rb:8 When I press go # calculador_steps.rb:12 undefined method `go' for #<Calc:0x11c5358 @y=10, @x=10> (NoMethodError) ./calculador_steps.rb:13:in `/^I press go$/' calculadora.feature:8:in `When I press go' Then I should see 100 # calculador_steps.rb:16 Failing Scenarios: cucumber calculadora.feature:6 # Scenario: Add two numbers 1 scenario (1 failed) 3 steps (1 failed, 1 skipped, 1 passed) 0m0.003s |
Olha, não temos ainda o metodo go, então vamos cirar-lo e fazer ele multiplicar os dois numeros.
1 2 3 | def go @x * @y end |
E rodando mais um vez nossa feature, passou!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | cucumber calculadora.feature Feature: Multiplication Operation In order to learn math As a math idiot I want to be told the multiply tow numbers Scenario: Add two numbers # calculadora.feature:6 Given I initialize calculator with 10 and 10 # calculador_steps.rb:11 When I press go # calculador_steps.rb:15 Then I should see 100 # calculador_steps.rb:19 TODO (Cucumber::Pending) ./calculador_steps.rb:20:in `/^I should see 100$/' calculadora.feature:9:in `Then I should see 100' 1 scenario (1 pending) 3 steps (1 pending, 2 passed) 0m0.003s |
Vamos para o ultimo mas não menos importante passo, agora queremos verificar se o resultado da multiplicacao que esta na variavel @result eh igual ao que esperamos que seja, então vamos implementar.
1 2 3 | Then /^I should see (.*)$/ do |result| @result.should == result.to_i end |
Aqui usamos o metodo should(deveria) para verificar se o resultado bate com o que definimos, se rodarmos o teste veremos que tudo passou.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | cucumber calculadora.feature Feature: Multiplication Operation In order to learn math As a math idiot I want to be told the multiply tow numbers Scenario: Add two numbers # calculadora.feature:6 Given I initialize calculator with 10 and 10 # calculador_steps.rb:11 When I press go # calculador_steps.rb:15 Then I should see 100 # calculador_steps.rb:19 1 scenario (1 passed) 3 steps (3 passed) 0m0.003s |
Lega, tudo passou! Obviamente isso eh muito superficial, mas acredito que isso possa ser uma boa introducao de como isso funciona, também não vimos nada profundamente de como o cucumber funciona, acredito que não seja o objetivo desse post(e eu nao sei muito tambem
), Novamente recomendo a leitura dos textos sobre bdd e afims.
Também fiz um pequeno screencast demostrando o que foi postado aqui:
Não deixe de ver o seguinte link:
http://en.wikipedia.org/wiki/Behavior_Driven_Development
Até mais pessoal e não deixem de comentar!