天天看點

The RSpec Book筆記《四》Describing Code with RSpec用RSpec描述代碼

在前面的章節,我們用cucumber從應用的層面,從外部描述codebreaker遊戲的行為。我們定義了步驟,但是留下一個異常,我們希望game傳回消息,但是傳回的卻是一個空數組。

在本章,我們将使用RSpec來描述更細粒度的行為,game對象執行個體的預期行為。

建立spec/codebreaker目錄

mkdir -p spec/codebreaker 

建立game_spec.rb檔案

vi spec/codebreaker/game_spec.rb 

輸入下面的内容

require 'spec_helper' 

module Codebreaker 

  describe Game do 

    describe "#start" do 

      it "sends a welcome message" 

      it "prompts for the first guess" 

    end 

  end 

end 

建立spec_helper.rb檔案

vi spec/spec_helper.rb 

require 'codebreaker' 

執行指令

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

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

      it "sends a welcome message" do 

        output=double('output') 

        game=Game.new(output) 

        output.should_receive(:puts).with('Welcome to Codebreaker!') 

        game.start 

      end 

rspec spec/codebreaker/game_spec.rb --color 

我們看到了紅色的錯誤。有的時候是邏輯錯誤,有的時候是其他錯誤。不管什麼錯誤,我們要把他們變成綠色的。

編輯檔案

更新内容

這下變成綠色了。

cucumber features/codebreaker_start_game.feature 

Then I should see "Welcome to Codebreaker!"也變成了綠色。

接下來我們繼續編輯spec/codebreaker/game_spec.rb檔案。

vi sepc/codebreaker/game_spec.rb 

替換第二個it中的内容。

it "prompts for the first guess" do 

       output=double("output") 

       game=Game.new(output) 

       output.should_receive(:puts).with("Enter guess:") 

       game.start 

     end 

然後再次執行

rspec spec/codebreaker/game_spec.rb  

還是有錯誤

.F 

Failures: 

  1) Codebreaker::Game#start prompts for the first guess 

     Failure/Error: game.start 

       Double "output" received :puts with unexpected arguments 

         expected: ("Enter guess:") 

              got: ("Welcome to Codebreaker!") 

     # ./lib/codebreaker/game.rb:8:in `start' 

     # ./spec/codebreaker/game_spec.rb:18:in `block (3 levels) in <module:Codebreaker>' 

Finished in 0.00178 seconds 

2 examples, 1 failure 

再次修改lib/codebreaker/game.rb

vi lib/codebreaker/game.rb 

添加

  class Game 

    def initialize(output) 

      @outputoutput=output 

    def start 

      @output.puts "Welcome to Codebreaker!" 

      @output.puts "Enter guess:" 

執行

rspec spec/codebreaker/game_spec.rb 

這下麻煩了,問題更大了,不僅第二個測試沒有通過,就連剛才通過的第一個也出現了問題。

兩次測試都沒有接收到應該接收到資訊,導緻測試都失敗了。

解決這個問題,有一個方法,就是as_null_object。

我們來修改一下game_spec.rb

内容如下

        output=double('output').as_null_object 

      it "prompts for the first guess" do 

        output=double("output").as_null_object 

        output.should_receive(:puts).with("Enter guess:") 

~      

這回好了,又出現了我們的綠色,測試都通過了。

是重構的時候了。

Martin Flower在重構一書中寫到。

重構,就是在不改變代碼外在行為的情況下,改進代碼的内部結構。

如何來確定外在行為沒有被改變呢?在每次改變之後,我們都會進行檢查。如果測試通過,那就說我們成功了。如果沒有通過,就要找出問題,甚至復原到前一個階段,來保證外在行為還保持在綠色狀态。

重構有一個作用,就是消除重複,提高重用。

在game_spec.rb中有兩行代碼

output=double("output").as_null_object  

game=Game.new(output)  

在兩次測試都出現了。我們來修改一下game_spec.rb,引入before(:each)。

      before(:each) do 

        @output=double('output').as_null_object 

        @game=Game.new(@output) 

        @output.should_receive(:puts).with('Welcome to Codebreaker!') 

        @game.start 

        @output.should_receive(:puts).with("Enter guess:") 

before(:each)中的代碼在每個測試運作之前都會執行。

如果隻是建立變量,給變量指派。在RSpec中我們可以使用let(:method){}來替代。

       let(:output) { double("output").as_null_object } 

       let(:game) { Game.new(output) } 

~     

重構完成了。跑一下測試,也是綠色的,沒有問題,重構成功了。

讓我們來做一個可執行的game。

mkdir bin 

vi bin/codebreaker

在檔案中輸入

#!/usr/bin/evn ruby 

$LOAD_PATH.unshift File.expand_path('../../lib',__FILE__) 

game=Codebreaker::Game.new(STDOUT) 

game.start 

讓我們執行以下

ruby bin/codebreaker 

看到了我們想要的内容。

Welcome to Codebreaker! 

Enter guess: 

總結

本章我們從一個cucumber的邏輯錯誤開始。從外部的cucumber周期,轉向内部的rspec周期。

本章我們使用RSpec來完成了一個red/green/refactor的周期。

這就是BDD的周期。從外到裡的驅動開發,從使用cucumber描述的業務scenario,到使用rspec描述的内部對象。

本文轉自 virusswb 51CTO部落格,原文連結:http://blog.51cto.com/virusswb/1060876,如需轉載請自行聯系原作者

繼續閱讀