The RSpec Book笔记《四》Describing Code with RSpec用RSpec描述代码

在前面的章节,我们用cucumber从应用的层面,从外部描述codebreaker游戏的行为。我们定义了步骤,但是留下一个异常,我们希望game返回消息,但是返回的却是一个空数组。

在本章,我们将使用RSpec来描述更细粒度的行为,game对象实例的预期行为。

创建spec/codebreaker目录
 

  
  
  
  
  1. mkdir -p spec/codebreaker 



创建game_spec.rb文件
 

  
  
  
  
  1. vi spec/codebreaker/game_spec.rb 



输入下面的内容
 

  
  
  
  
  1. require 'spec_helper' 
  2.  
  3. module Codebreaker 
  4.   describe Game do 
  5.     describe "#start" do 
  6.       it "sends a welcome message" 
  7.       it "prompts for the first guess" 
  8.     end 
  9.   end 
  10. end 



创建spec_helper.rb文件
 

  
  
  
  
  1. vi spec/spec_helper.rb 



输入下面的内容
 

  
  
  
  
  1. require 'codebreaker' 



执行命令

  
  
  
  
  1. rspec spec/codebreaker/game_spec.rb --format doc 



修改spec/codebreaker/game_spec.rb的内容
 

  
  
  
  
  1. require 'spec_helper' 
  2.  
  3. module Codebreaker 
  4.   describe Game do 
  5.     describe "#start" do 
  6.       it "sends a welcome message" do 
  7.         output=double('output'
  8.         game=Game.new(output) 
  9.         output.should_receive(:puts).with('Welcome to Codebreaker!'
  10.  
  11.         game.start 
  12.       end 
  13.       it "prompts for the first guess" 
  14.     end 
  15.   end 
  16. end 


执行命令
 

  
  
  
  
  1. rspec spec/codebreaker/game_spec.rb --color 



我们看到了红色的错误。有的时候是逻辑错误,有的时候是其他错误。不管什么错误,我们要把他们变成绿色的。

编辑文件
 

  
  
  
  
  1. vi spec/codebreaker/game_spec.rb 



更新内容
 

  
  
  
  
  1. require 'spec_helper' 
  2.  
  3. module Codebreaker 
  4.   describe Game do 
  5.     describe "#start" do 
  6.       it "sends a welcome message" do 
  7.         output=double('output'
  8.         game=Game.new(output) 
  9.         output.should_receive(:puts).with('Welcome to Codebreaker!'
  10.  
  11.         game.start 
  12.       end 
  13.       it "prompts for the first guess" 
  14.     end 
  15.   end 
  16. end 



执行命令
 

  
  
  
  
  1. rspec spec/codebreaker/game_spec.rb --color 



这下变成绿色了。

执行命令
 

  
  
  
  
  1. cucumber features/codebreaker_start_game.feature 



Then I should see "Welcome to Codebreaker!"也变成了绿色。

接下来我们继续编辑spec/codebreaker/game_spec.rb文件。

 

  
  
  
  
  1. vi sepc/codebreaker/game_spec.rb 



替换第二个it中的内容。
 

  
  
  
  
  1. it "prompts for the first guess" do 
  2.        output=double("output"
  3.        game=Game.new(output) 
  4.        output.should_receive(:puts).with("Enter guess:"
  5.  
  6.        game.start 
  7.      end 

然后再次执行

 

  
  
  
  
  1. rspec spec/codebreaker/game_spec.rb  

还是有错误

 

  
  
  
  
  1. .F 
  2.  
  3. Failures: 
  4.  
  5.   1) Codebreaker::Game#start prompts for the first guess 
  6.      Failure/Error: game.start 
  7.        Double "output" received :puts with unexpected arguments 
  8.          expected: ("Enter guess:") 
  9.               got: ("Welcome to Codebreaker!") 
  10.      # ./lib/codebreaker/game.rb:8:in `start' 
  11.      # ./spec/codebreaker/game_spec.rb:18:in `block (3 levels) in <module:Codebreaker>
  12.  
  13. Finished in 0.00178 seconds 
  14. 2 examples, 1 failure 

再次修改lib/codebreaker/game.rb

 

  
  
  
  
  1. vi lib/codebreaker/game.rb 

添加

 

  
  
  
  
  1. module Codebreaker 
  2.   class Game 
  3.     def initialize(output) 
  4.       @outputoutput=output 
  5.     end 
  6.  
  7.     def start 
  8.       @output.puts "Welcome to Codebreaker!" 
  9.       @output.puts "Enter guess:" 
  10.     end 
  11.   end 
  12. end 

执行

 

  
  
  
  
  1. rspec spec/codebreaker/game_spec.rb 

这下麻烦了,问题更大了,不仅第二个测试没有通过,就连刚才通过的第一个也出现了问题。

两次测试都没有接收到应该接收到信息,导致测试都失败了。

解决这个问题,有一个方法,就是as_null_object。

我们来修改一下game_spec.rb

 

  
  
  
  
  1. vi spec/codebreaker/game_spec.rb 

内容如下

 

  
  
  
  
  1. require 'spec_helper' 
  2.  
  3. module Codebreaker 
  4.   describe Game do 
  5.     describe "#start" do 
  6.       it "sends a welcome message" do 
  7.         output=double('output').as_null_object 
  8.         game=Game.new(output) 
  9.         output.should_receive(:puts).with('Welcome to Codebreaker!'
  10.  
  11.         game.start 
  12.       end 
  13.       it "prompts for the first guess" do 
  14.         output=double("output").as_null_object 
  15.         game=Game.new(output) 
  16.         output.should_receive(:puts).with("Enter guess:"
  17.  
  18.         game.start 
  19.       end 
  20.     end 
  21.   end 
  22. end 
  23. ~      

执行命令

 

  
  
  
  
  1. rspec spec/codebreaker/game_spec.rb --color 

这回好了,又出现了我们的绿色,测试都通过了。

是重构的时候了。

Martin Flower在重构一书中写到。

重构,就是在不改变代码外在行为的情况下,改进代码的内部结构。

如何来确保外在行为没有被改变呢?在每次改变之后,我们都会进行检查。如果测试通过,那就说我们成功了。如果没有通过,就要找出问题,甚至回滚到前一个阶段,来保证外在行为还保持在绿色状态。

重构有一个作用,就是消除重复,提高重用。

在game_spec.rb中有两行代码

 

  
  
  
  
  1. output=double("output").as_null_object  
  2. game=Game.new(output)  

在两次测试都出现了。我们来修改一下game_spec.rb,引入before(:each)。

 

  
  
  
  
  1. require 'spec_helper' 
  2.  
  3. module Codebreaker 
  4.   describe Game do 
  5.     describe "#start" do 
  6.  
  7.       before(:eachdo 
  8.         @output=double('output').as_null_object 
  9.         @game=Game.new(@output) 
  10.       end 
  11.     
  12.       it "sends a welcome message" do 
  13.         @output.should_receive(:puts).with('Welcome to Codebreaker!'
  14.  
  15.         @game.start 
  16.       end 
  17.       it "prompts for the first guess" do 
  18.         @output.should_receive(:puts).with("Enter guess:"
  19.  
  20.         @game.start 
  21.       end 
  22.     end 
  23.   end 
  24. end 

before(:each)中的代码在每个测试运行之前都会执行。

如果只是创建变量,给变量赋值。在RSpec中我们可以使用let(:method){}来替代。

 

  
  
  
  
  1. require 'spec_helper' 
  2.  
  3. module Codebreaker 
  4.   describe Game do 
  5.     describe "#start" do 
  6.  
  7.        let(:output) { double("output").as_null_object } 
  8.        let(:game) { Game.new(output) } 
  9.  
  10.       it "sends a welcome message" do 
  11.         output.should_receive(:puts).with('Welcome to Codebreaker!'
  12.  
  13.         game.start 
  14.       end 
  15.       it "prompts for the first guess" do 
  16.         output.should_receive(:puts).with("Enter guess:"
  17.  
  18.         game.start 
  19.       end 
  20.     end 
  21.   end 
  22. end 
  23. ~     

重构完成了。跑一下测试,也是绿色的,没有问题,重构成功了。

让我们来做一个可执行的game。

 

  
  
  
  
  1. mkdir bin 
  2. vi bin/codebreaker

在文件中输入

 

  
  
  
  
  1. #!/usr/bin/evn ruby 
  2. $LOAD_PATH.unshift File.expand_path('../../lib',__FILE__) 
  3. require 'codebreaker' 
  4.  
  5. game=Codebreaker::Game.new(STDOUT) 
  6. game.start 

让我们执行以下

 

  
  
  
  
  1. ruby bin/codebreaker 

看到了我们想要的内容。

 

  
  
  
  
  1. Welcome to Codebreaker! 
  2. Enter guess: 

总结

本章我们从一个cucumber的逻辑错误开始。从外部的cucumber周期,转向内部的rspec周期。

本章我们使用RSpec来完成了一个red/green/refactor的周期。

这就是BDD的周期。从外到里的驱动开发,从使用cucumber描述的业务scenario,到使用rspec描述的内部对象。

 


教程参考:The RSpec Book


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

你可能感兴趣的:(rspec,cucumber,codebreaker,描述代码)