在前面的章節,我們用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,如需轉載請自行聯系原作者