每一位程式員都討厭測試,相較於寫程式時那種解題的快感,測試簡直百般無聊。那麼,為什麼要測試?目的是為了程式碼的品質,品質不單單是測試人員的事,那是程式員基本的責任。
測試等於是一種背書,證明程式的任何環節毫無問題;測試可以帶給程式員信心,所以只要是我寫的程式,我就願意負責,而且我一定會對我寫的程式感到驕傲!!
先看看為何要用RSpec來進行測試,難道在Rails裡頭用他基本的unit test來作不行嗎?當然可以,只是測試的風格不同而已,但我認為風格很重要,風格可以改造想法,而想法可以改造程式!!
這是一段用Rails unit test寫出來的測試程式:
class MessageTest < Test::Unit::TestCase
def test_create
msg = Message.create(:text => "test text")
assert msg.save
end
end這種測試基本上還是在寫程式,太貼近程式的想法,無法把自己置身事外,當作一名測試員。
這是一段用RSpec寫出來檢查是否自動產生訊息主鍵代碼的測試程式,我需要說明他在做什麼嗎?完全不需要!!
describe Message do
before do
@message = Message.new
end
it "should auto generate uuid" do
@message.text = "test message"
@message.save
@message.uuid.should_not be_empty
end
end
相較於普通unit test裡頭常見的語法assert_equal(e1, e2),RSpec最大的優點就是相當白話,一目了然。
assert_equal的壞處不用我講,那個e1, e2常常擺錯邊,老是忘記誰要檢查誰。
當測試這件事情變得平易近人之後,程式員自然會投資時間下去做,因為未來一定會得到自信與驕傲。
要使用RSpec得先安裝幾個gem:
sudo gem install rspec然後切換到你的Rails project安裝幾個plugin:
script/plugin install -x svn://rubyforge.org/var/svn/rspec/tags/CURRENT/rspec script/plugin install -x svn://rubyforge.org/var/svn/rspec/tags/CURRENT/rspec_on_rails我通常會將RSpec與AutoTest併用:
sudo gem install ZenTest script/plugin install -x http://svn.caldersphere.net/svn/main/plugins/rspec_autotest一切就緒之後,再來就是產生model, controller的測試框架:
script/generate rspec script/generate rspec_model message script/generate rspec_controller message這裡的message是測試的對象,例如要寫測試程式的對象是一個model,這個model的名字叫做message,就是去generate一個rspec_model;要測一支controller則是去generate一支rspec_controller。
產生完畢之後,你會發現Rails project底下多了個spec目錄,而spec目錄裡頭則有許多諸如controller, model, view等子目錄,進去model一看,裡頭躺著一支剛產生好的message_spec.rb,如下:
describe Message do
before do
@message = Message.new
end
it "should description" do
end
end這就是測試程式初始的框架,你可以先簡單描述他,然後慢慢補上測試細節,如下:describe Message do
before do
@message = Message.new
end
it "should validate presence of text" do
end
it "should auto generate uuid" do
end
it "should update Twitter status" do
pending "not yet"
end
end也就是說我的這個訊息類別,必須要檢查是否有內文、必須要自動產生uuid,而最後必須要去更新我的Twitter狀態。因為更新Twitter的API目前還不知道該如何使用,所以我先pending。
實際進行測試之前,請記得先建立測試資料庫,並把development資料庫架構複製一份到test資料庫來。
rake db:test:clone_structure
我通常會在背景跑一支autotest程式,只要我一修改程式存檔,他就自動幫我跑測試程式,方便得很。要執行autotest請在Rails project目錄下,執行:
autotest -rails接著autotest就會默默的在背景跑,並且告訴您總共有幾支example(每個it描述算一個example)、幾支通過、幾支失敗、幾支pending(例如update Twitter status就是一支暫不處理的example)。所以這裡的輸出就是:
3 examples, 0 failures, 1 pending然後他又繼續在背景等,等你下次修改、存檔,又繼續自動幫您進行測試。
接著,補上一些細節:
describe Message do
before do
@message = Message.new
end
it "should validate presence of text" do
lambda {
@message.save.should_not be_true
}.should_not change(Message, :count)
@message.text = "test message"
@message.save.should be_true
end
it "should auto generate unique and immutable uuid" do
lambda {
@comment.save
}.should change(@comment, :uuid).from(nil)
lambda {
@comment.uuid = "8fa82ce9-8f61-11dc-a4f7-000a95c72214"
}.should raise_error(ActiveRecord::RecordNotSaved)
end
it "should update Twitter status" do
pending "not yet"
end
end每個it後面寫的是白話的功能描述,也就是這項測試必須必須達成什麼要求,日後可以運用rdoc直接把這些描述轉成文件;describe則可以當作一種分類,如果有其他不同種類的message需要測試,也可以這樣寫:describe Message, "with comments" do
before do
@message = Message.new
end
it "should be replied using comment" do
end
endshould或should_not其實是類似assert,只不過unit test採用:assert(e1, e2) # 動詞(主詞1, 主詞2)的模式,常常搞不清楚兩個主詞之間的關係與意義;而RSpec採用:
message.should be_true # 主詞.should_be 結果如此一來將更為直覺及口語化。
RSpec的優點當然不只直覺、簡單及口語化(難道這還不夠?),還可以運用mock將model從controller中抽離,另外,搭配TextMate甚至可以提供更多快速而且簡單的玩法,千萬不可錯過啊。
另外,新版ZenTest已經將許多好用的外掛直接整合進來,例如: menu, datetime, redgreen, growl等,現在都變成豪華的內裝配備。所以,舉凡測試開始、測試成功與測試失敗都會透過Growl來作提示。
要是您嫌預設的Growl提示圖案不好看的話,請輸入以下指令進行修改:
sudo mate /Library/Ruby/Gems/1.8/gems/ZenTest-3.6.1/lib/autotest/growl.rb請修改此處:
def self.growl title, msg, pri=0
title += " in #{Dir.pwd}"
msg += " at #{Time.now}"
# system "growlnotify -n autotest --image /Applications/Mail.app/Contents/Resources/Caution.tiff -p #{pri} -m #{msg.inspect} #{title}"
system "growlnotify -n autotest --image /Users/winson/Pictures/rails.png -p #{pri} -m #{msg.inspect} #{title}"
end
