Rspec簡介

英文原文:An introduction to Rspec

      在ruby世界中有許多好用的測試框架,rspec算是其中比較流行的一個。rspec使用了和直接測試方法的不同測試思路——測試應用的行爲。下面我就來解釋一下怎樣實用rspec來測試應用。

Rspec入門

    RSpec使用describe塊及其近親context塊來封裝測試代碼。通常在單元測試時我們使用describe來描述類的行爲:
describe Hash do
 
end

    it塊被用來編寫具體測試代碼。下面這個例子就是爲Hash類編寫的一個spec測試:
describe Hash do
  it "should return a blank instance" do
    Hash.new.should == {}
  end
end

   上面就是Rspec的基礎示例了。在用命令gem install rsepc安裝好rspec,然後將上面的代碼放入hash_spec.rb文件,鍵入下面的命令執行測試:
$ rspec hash_spec.rb
    測試的輸出結果如下:
.
 
Finished in 0.11021 seconds
1 example, 0 failures

    你也可以實用before和after來設置測試前後需要執行的代碼,在before塊和after塊中的設置在describe塊中也有效:
describe Hash do
  before do
    @hash = Hash.new({:hello => 'world'})
  end
 
  it "should return a blank instance" do
    Hash.new.should == {}
  end
 
  it "hash the correct information in a key" do
    @hash[:hello].should == 'world'
  end
end

    上面的代碼會在每個測試塊執行前創建一個 @hash變量。before塊可以攜帶兩個參數——all(一次性爲所有的測試塊設置變量)或each(每個測試塊單獨設置變量)。after塊和before塊一樣也有這兩個參數,不同的僅僅是after是在測試塊代碼之後執行。在前一個測試執行結束需要銷燬起狀態時after塊很有用。

RSpec術語

    通常,我們使用describe來描述方法。在方法名前使用點號“.”表示測試的是類方法,實用“#”表示測試的是實例方法,如下所示:
describe MyClass do
  describe ".class_method_1" do
  end
 
  describe "#instance_method_1" do
  end
end

    context方法主要用來將一羣具有相同上下文的測試集合在一起。當使用複雜的setup和teardown代碼來設置對象時特別有用。下面以真實生活中的漢堡類爲例。

     假設我們要測試漢堡類Burger的#apply_ketchup方法。然而有的人卻不想加番茄醬,於是我們爲這兩種人分別編寫測試:
describe Burger do
  describe "#apply_ketchup" do
    context "with ketchup" do
      before do
        @burger = Burger.new(:ketchup => true)
        @burger.apply_ketchup
      end
 
      it "sets the ketchup flag to true" do
        @burger.has_ketchup_on_it?.should be_true
      end
    end
 
    context "without ketchup" do
      before do
        @burger = Burger.new(:ketchup => false)
        @burger.apply_ketchup
      end
 
      it "sets the ketchup flag to false" do
        @burger.has_ketchup_on_it?.should be_false
      end
    end
  end
end

整理一下

    上面的代碼可以正常工作,但是卻有點重複了。RSpec爲我們提供了一些helper來簡化代碼。我們可以使用let關鍵字來重寫上述代碼,使得變量在第一次被訪問時能夠自動創建:
describe Burger do
  describe "#apply_ketchup" do
    context "with ketchup" do
      let(:burger) { Burger.new(:ketchup => true) }
      before  { burger.apply_ketchup }
 
      it "sets the ketchup flag to true" do
        burger.has_ketchup_on_it?.should be_true
      end
    end
 
    context "without ketchup" do
      let(:burger) { Burger.new(:ketchup => false) }
      before  { burger.apply_ketchup }
 
      it "sets the ketchup flag to false" do
        burger.has_ketchup_on_it?.should be_false
      end
    end
  end
end

     代碼變得好看了些,但我們可以使用subject方法來進一步簡化。subject指定的對象是rspec目前正在進行的測試針對的對象。下面我將結合specify方法使用它。specify方法和it十分相似,唯一區別是specify將測試內容作爲測試描述:
describe Burger do
  describe "#apply_ketchup" do
    subject { burger }
    before  { burger.apply_ketchup }
 
    context "with ketchup" do
      let(:burger) { Burger.new(:ketchup => true) }
 
      specify { subject.has_ketchup_on_it?.should be_true }
    end
 
    context "without ketchup" do
      let(:burger) { Burger.new(:ketchup => true) }
 
      specify { subject.has_ketchup_on_it?.should be_false }
    end
  end
end

    按照rspec的約定,它會尋找以has開頭並且以問號結尾的方法,下面是最終的burger_spec.rb:
class Burger
  attr_reader :options
 
  def initialize(options={})
    @options = options
  end
 
  def apply_ketchup
    @ketchup = @options[:ketchup]
  end
 
  def has_ketchup_on_it?
    @ketchup
  end
end
 
 
describe Burger do
  describe "#apply_ketchup" do
    subject { burger }
    before  { burger.apply_ketchup }
 
    context "with ketchup" do
      let(:burger) { Burger.new(:ketchup => true) }
 
      it { should have_ketchup_on_it }
    end
 
    context "without ketchup" do
      let(:burger) { Burger.new(:ketchup => false) }
 
      it { should_not have_ketchup_on_it }
    end
  end
end

漢堡需要的不僅僅是番茄醬

      在這個簡短的教程中我們創建了burger類並用rspec進行了測試,並且我們根據rspec的習慣使測試代碼更符合rspec的風格。我們的目的是爲了更好的可讀性和更好的運行代碼。以後我會書寫更多的符合rspec的測試,但現在,該吃午飯了。

------------------------------------------原文結束-------------------------------------------------------------

附:RSpec斷言規則

    RSpec有一些常見的斷言規則。Ruby的斷言方法是以問號結尾並且返回true或false的方法,常見的如: empty?   nil?    instance_of? 等。在spec中的斷言很簡單,就是should be_去掉問號的斷言方法。如:
[].should be_empty => [].empty? #passes
  [].should_not be_empty => [].empty? #fails

    除了用"be_"來前綴斷言方法,也可以用"be_a_"和"be_an_"前綴,使得代碼讀起來更加自然:
"a string".should be_an_instance_of(String) =>"a string".instance_of?(String) #passes

  3.should be_a_kind_of(Fixnum) => 3.kind_of?(Numeric) #passes
  3.should be_a_kind_of(Numeric) => 3.kind_of?(Numeric) #passes
  3.should be_an_instance_of(Fixnum) => 3.instance_of?(Fixnum) #passes
  3.should_not be_instance_of(Numeric) => 3.instance_of?(Numeric) #fails

     Rspec也會爲諸如“has_key?”之類的斷言創建匹配器,要使用該特性,在斷言對象上使用 should have_key(:key) 就可以了,rspec會自動在對象上調用has_key?(:key)。如:
{:a => "A"}.should have_key(:a) => {:a => "A"}.has_key?(:a) #passes
  {:a => "A"}.should have_key(:b) => {:a => "A"}.has_key?(:b) #fails

     還有一些常見的斷言方法如: be  be_close  change  eql  have  be_true  be_false be_nil include raise_error respond_to throw_symbol   等等


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章