2012-06-02 16 views
5

我試圖讓我的規範保持乾淨和乾爽,但我對除測試API的哪個版本以外的API相同的API進行了一些測試。我可以重複的規格簡單地使用這樣的事情:重複使用不同參數的RSpec示例組

%w(v1 v2).each do |version| 
    describe "Query #{version} API" do 
    it "responds with JSON" 
     # make the call using the version 
    end 
    end 
end 

,但我想的東西有點清潔,所以我寫了這個方法:

module RepetitivelyDescribe 
    def repetitively_describe(*args, &example_group_block) 
    options = args.extract_options! 
    options.delete(:for).each do |item| 
     item_args = args.collect(&:dup) + [options.dup] 
     item_args[0] << " [#{item}]" 

     describe(*item_args) do 
     example_group_block.call item 
     end 
    end 
    end 
end 

RSpec::Core::ExampleGroup.extend RepetitivelyDescribe 

然後我的測試可以看看更多這樣的:

repetitively_describe "Query API", :for => %(v1 v2) do |version| 
    it "responds with JSON" 
    # make the call using the version 
    end 
end 

我意識到這是迂腐一點點,但它縮進少了一個級別,如果我將要作出這個調用了很多,我想有它清潔器。

但是,當然,它並不像我想的那樣工作。在我的repetitively_describe內對describe的調用沒有被記錄到RSpec輸出(當使用文檔格式輸出時),儘管內部的例子得到了重複並且按照預期使用了版本塊參數。實質上,這種上下文級別會丟失(保留repetitively_describe塊外部和內部的describe塊)。

如果需要,還有更詳細的示例代碼a gist。任何線索,爲什麼這不是正確的工作?

+0

我個人使用共享上下文或共享示例組。 「it_behaves_like」查詢API「do let(:version){:v1}結束」。 https://www.relishapp.com/rspec/rspec-core/docs/example-groups/shared-examples https://www.relishapp.com/rspec/rspec-core/v/2-9/docs/example -groups/shared-context – d11wtq

回答

5

因此(道歉,如果我重複你已經知道的東西),但每次調用describe/context時,rspec會創建一個新類,它是當前示例組類的子類(最終是RSpec::Core::ExampleGroup的子類),然後使用module_eval來評估該類上下文中的塊。如果我運行

describe "foo" do 
    puts "#{self}; #{self.superclass}" 
    describe "behaviour 1" do 
    puts "#{self}; #{self.superclass}" 
    context "with x" do 
     puts "#{self}; #{self.superclass}" 
    end 
    end 
end 

然後輸出

#<Class:0x007fb772bfbc70>; RSpec::Core::ExampleGroup 
#<Class:0x007fb772bfb180>; #<Class:0x007fb772bfbc70> 
#<Class:0x007fb772bfa5f0>; #<Class:0x007fb772bfb180> 

當你調用it rspec的創建Example對象,並將其追加自我類實例變量(當前實驗組)。 rspec在示例的元數據中也粘貼當前的示例組,向上遍歷這個示例組的樹就是給出了示例的完整描述。

您的repetitively_describe方法調用describe,因此在您調用example_group_block.call item時,self確實是新創建的示例組。當PROC獲取評估,它當然會記住的self的值,當它被調用,所以你要it調用到,這是當前的例子組提出時repetitively_describe(灑一些調用容易驗證的整個檢查自身的價值你的代碼)。類似地,描述調用將示例組添加爲外部示例組的子組,而不是由repetitively_describe創建的組。

什麼,當然你需要做的就是調用example_group_block保持自我的正確值。

module RepetitivelyDescribe 
    def repetitively_describe(*args, &example_group_block) 
    options = args.extract_options! 
    options.delete(:for).each do |item| 
     item_args = args.collect(&:dup) + [options.dup] 
     item_args[0] << " [#{item}]" 

     describe(*item_args) do 
     class_exec(item, &example_group_block) 
     end 
    end 
    end 
end 

這種變化的

describe('foo') do 
    repetitively_describe "Query API", :for => %w(v1 v2) do |version| 
    it "responds with JSON" 
    end 
end.descendant_filtered_examples.collect(&:full_description) 

輸出["foo Query API [v1] responds with JSON", "foo Query API [v2] responds with JSON"]代替["foo responds with JSON", "foo responds with JSON"]改變之前。

+0

完美,謝謝 - 我也很欣賞所有的細節! – pat

+0

實際上,這並不完美:在內部例子中,'repetitively_describe'塊中定義的方法是不可用的。有什麼想法嗎? – pat

+1

啊是的,你需要class_exec而不是instance_exec - 在你做'def blah; end'在塊內 –

相關問題