Automating Features with cucumber

cucumber

 

 
  
  1. Given /^I am not yet playing$/ do 
  2.   pending # express the regexp above with the code you wish you had 
  3. end 


这就是一个cucumber的步骤定义。你可以把它想成是一个方法,然后在definition中定义一个这样的方法。

 

 
  
  1. mkdir features/step_definitions 
  2.  
  3. vi features/step_definitions/codebreaker_steps.rb 



把上面的内容拷贝进去,并且删除pending一行。

 

 
  
  1. Given /^I am not yet playing$/ do 
  2.   
  3. end 



执行下面的命令

 

 
  
  1. cucumber features/codebreaker_start_game.feature 
 
  
  1. Feature: code-breaker starts game 
  2.   
  3.   As a code-breaker 
  4.   I want to start a game 
  5.   So that I can break the code 
  6.  
  7.   Scenario: start game                            # features/codebreaker_start_game.feature:7 
  8.     Given I am not yet playing                    # features/step_definitions/codebreaker_steps.rb:1 
  9.     When I start a new game                       # features/codebreaker_start_game.feature:9 
  10.     Then I should see "Welcome to Codebreaker!"   # features/codebreaker_start_game.feature:10 
  11.     And I should see "Enter guess:"               # features/codebreaker_start_game.feature:11 
  12.     But I should not see "What is your question?" # features/codebreaker_start_game.feature:12 
  13.  
  14. 1 scenario (1 undefined) 
  15. 5 steps (4 undefined, 1 passed) 
  16. 0m0.005s 
  17.  
  18. You can implement step definitions for undefined steps with these snippets: 
  19.  
  20. When /^I start a new game$/ do 
  21.   pending # express the regexp above with the code you wish you had 
  22. end 
  23.  
  24. Then /^I should see "(.*?)"$/ do |arg1| 
  25.   pending # express the regexp above with the code you wish you had 
  26. end 
  27.  
  28. Then /^I should not see "(.*?)"$/ do |arg1| 
  29.   pending # express the regexp above with the code you wish you had 
  30. end 


结果告诉我们有一个scenario,五个步骤,四个还没有定义,一个已经通过。

cucumber命令的参数是features/codebreaker_start_game.feature,在cucumber启动的时候会加载目录下所有的.rb文件,已经子目录下面的.rb文件,包括前面的features/step_definitions/codebreaker_steps.rb。

我们通过调用cucumber提供的五个方法来定义feature中的步骤:Given(), When(), Then(), And(), But()。And()和But()会呈现跟在前面的Given, When, Then的意思。在上面的例子中,scenario中最后两行的And和But被当做是Then来处理。

每个方法都通过正则表达来匹配。

Given方法对应的正则表达式是:/^I am not yet playing$/。就会匹配feature中的Given部分。

When方法中需要我们创建一个新游戏,然后开始这个游戏。

 

 
  
  1. When /^I start a new game$/ do 
  2.   Codebreaker::Game.new.start 
  3. end 



这时候,我们的应用还没有任何代码,我们只是按照我们想要的方式来写。我们希望简单,尽可能的简单。
The code you wish you had.

再次运行
 

 
  
  1. cucumber features/codebreaker_start_game.feature  


你会发现一些红色的信息
uninitialized constant Codebreaker (NameError)

因为你还没有定义Codebreaker以及Game以及start方法。好吧,我们来定义一下。
 

 
  
  1. mkdir -p lib/codebreaker/ 
  2. vi lib/codebreaker/game.rb 



输入下面的内容

 
  
  1. module Codebreaker 
  2.   class Game 
  3.     def start 
  4.  
  5.     end 
  6.   end 
  7. end 



再次执行
 

 
  
  1. cucumber features/codebreaker_start_game.feature  



还是出现之前的红色信息。

这是因为按照约定,在lib目录需要有一个以顶级module名称命名的文件。

 

 
  
  1. vi lib/codebreaker.rb 



写入下面的内容
 

 
  
  1. require 'codebreaker/game' 



 

 
  
  1. vi features/support/env.rb  



写入下面的内容
 

 
  
  1. $LOAD_PATH << File.expand_path('../../../lib', __FILE__) 
  2. require 'codebreaker' 


再次执行
 

 
  
  1. cucumber features/codebreaker_start_game.feature  



看到的内容又变绿色了。

在完成第二个步骤之后,我们来看看Then这个步骤。

在这个步骤中,我们希望在console中看到Welcome to Codebreaker!。这意味着我们需要一个工具,来捕获Game发送到STDOUT的信息。

 

 
  
  1. vi features/step_definitions/codebreaker_steps.rb 



加入下面的内容
 

 
  
  1. Then /^I should see "(.*?)"$/ do |message| 
  2.   output.messages.should include(message) 
  3. end 



修改When的内容
 

 
  
  1. When /^I start a new game$/ do 
  2.   game=Codebreaker::Game.new(output) 
  3.   game.start 
  4. end 



增加下面的内容
 

 
  
  1. class Output 
  2.   def messages 
  3.     @messages ||= [] 
  4.   end 
  5.  
  6.   def puts(message) 
  7.     messages << message 
  8.   end 
  9. end 
  10.  
  11. def output 
  12.   @output ||= Output.new 
  13. end 



再次执行
 

 
  
  1. cucumber features/codebreaker_start_game.feature  


提示我们wrong number of arguments(1 for 0)(ArgumentError)

是因为我没有定义带有参数的initialize。

 

 
  
  1. vi lib/codebreaker/game.rb  



用下面的内容替换

 

 
  
  1. module Codebreaker 
  2.   class Game 
  3.     def initialize(output) 
  4.  
  5.     end 
  6.  
  7.     def start 
  8.  
  9.     end 
  10.   end 
  11. end 



再次执行
 

 
  
  1. cucumber features/codebreaker_start_game.feature  



这时候提示我们有逻辑错误,因为我们的game还没有完成。我们会在后面完成。

总结

我们先在.feature文件中写一个feature的scenario,已经scenario的steps,然后在step_definitions中定义Given,When,Then,然后在通过测试cucumber .feature文件来驱动我们编写实现代码。

当目前为止,我们已经学会使用cucumber从外部描述一件事。在接下来的章节中,我们将会进行从外到里的工作方式,使用RSpec来驱动单个对象的外部行为。


教程参考:The RSpec Book


所有的代码放在
https://github.com/woaigithub/the-rspec-book-with-cucumber