2011-09-19 35 views
1

我使用Ruby on Rails的3.0.9和RSpect 2.我想通過以下方式重構了一些規範文件(爲了用更少的代碼相似User類對象來測試屬性值):指定明確的'主題?

describe User do 
    let(:user1) { Factory(:user, :users_attribute_a => 'invalid_value') } 
    let(:user2) { Factory(:user, :users_attribute_b => 'invalid_value') } 
    let(:user3) { Factory(:user, :users_attribute_c => 'invalid_value') } 

    it "foreach user" do 
    [ user1, user2, user3 ].each do |user| 
     subject { user } 

     it "should be whatever" 
     user.should_not be_valid 
     ... 
     end 
    end 
    end 
end 

但是,如果我運行上面的測試中,我得到以下錯誤:

Failure/Error: it "should be whatever" do 
    NoMethodError: 
    undefined method `it' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_2::Nested_2:0x00000106ccee60> 

問題是什麼?我該如何解決這個問題?


@Emily答案後更新

如果在上面的代碼中,我使用的context "foreach user" do ...代替it "foreach user" do ...我得到以下錯誤:

undefined local variable or method `user1' for #<Class:0x00000105310758> (NameError) 
+0

僅供參考,使用'subject'是一個紅鯡魚在這裏。你從來沒有真正使用你在你的例子中設置的主題。 – Emily

回答

1

你在混合和匹配各種rspec的東西。這裏是你的東西,固定:

describe User do 
    let(:user1) { Factory(:user, :users_attribute_a => 'invalid_value') } 
    let(:user2) { Factory(:user, :users_attribute_b => 'invalid_value') } 
    let(:user3) { Factory(:user, :users_attribute_c => 'invalid_value') } 

    it "should not be valid" do 
    [ user1, user2, user3 ].each do |user| 
     user.should_not be_valid 
    end 
    end 
end 

我會做這種方式:

describe User do 
    subject{Factory.build(:user)} 
    it "should not be valid with invalid users_attribute_a" do 
    subject.users_attribute_a = "invalid_value" 
    subject.should_not be_valid 
    end 
    it "should not be valid with invalid users_attribute_b" do 
    subject.users_attribute_b = "invalid_value" 
    subject.should_not be_valid 
    end 
end 
  • 如果你想有「背景」,然後冷卻,但在此之前你不能有變數的上下文中的上下文。
  • 如果你想有一個規範,那麼有一個,但你不能淨「它」的語句

更新與儘可能少的代碼

describe User do 

    it "should not be valid with other attributes" do 
    {:users_attribute_a => 'invalid_value', :users_attribute_b => 'invalid_value', :users_attribute_c => 'invalid_value'}.each do |key, value| 
     Factory.build(:user, key => value).should_not be_valid 
    end 
    end 

end 
+0

什麼是在第二個模塊中重構代碼的方法,以便編寫更少的代碼? – Backo

+0

用我能想到的最少代碼更新 –

+1

您可以使用'let'將變量篩選到上下文中, – iain

2

的問題是有嵌套在另外一個規格。您需要用context "foreach user"替換it "foreach user"

編輯補充:經過一番調查後,它看起來像let設置助手僅僅是it "should ..."塊內可用,而不是周圍的環境。我建議嘗試找到不同的結構解決方案。最好的解決方案將取決於你實際試圖測試的內容。我猜你要做的是在刪除任何必需的屬性時確保用戶無效。在這種情況下,我做了什麼是這樣的:

describe User do 
    let(:user_attributes){ Factory.attributes_for(:user) } 

    # Testing missing values aren't valid 
    [:name, :email, :phone].each do |required_attribute| 
    it "should not be valid without #{required_attribute}" do 
     User.new(user_attributes.except(required_attribute)).should_not be_valid 
    end 
    end 

    # Testing invalid values aren't valid 
    [[:email, 'not_an_email'], [:phone, 'not a phone']].each do |(attribute, value)| 
    it "should not be valid with bad value for #{attribute}" do 
     User.new(user_attributes.update(attribute => value)).should_not be_valid 
    end 
    end 
end 

如果你正在做的事情,需要在您正在創建的實例更復雜的差異,有可能不是一個乾淨的方式來做到這一點與迭代。我認爲DRY在測試中不像測試的其他部分那麼重要。對於三種用戶類型有三個不同的上下文沒有任何錯誤,並且在每個上下文中都有一個有效性測試。

describe User do 
    context "with user1" do 
    subject{ Factory(:user, :users_attribute_a => 'invalid_value') } 
    it{ should_not be_valid } 
    end 

    context "with user2" do 
    subject{ Factory(:user, :users_attribute_b => 'invalid_value') } 
    it{ should_not be_valid } 
    end 

    context "with user3" do 
    subject{ Factory(:user, :users_attribute_c => 'invalid_value') } 
    it{ should_not be_valid } 
    end 
end 
+0

我更新了問題。 – Backo

0

問題是,使用「let」設置的助手不存在於示例上下文之外。

你想要做什麼,可以實現爲:

it "does something with all users" do 
    [user1, user2, user3] do |user| 
    user.valid?.should be_true 
    end 
end 

兩種情況下是不同的

另一種方式它可能工作(沒試過)是這樣的:

context "for all users" do 
    [:user1, :user2, :user3].each do |user| 
    it "does something" do 
     send(user).valid?.should be_true 
    end 
    end 
end 
0

這應該工作。注意如何編寫上下文,它會使測試的輸出更清晰。從這樣寫它意味着(對我而言)你應該單獨測試每個屬性,但它是你的選擇:

describe User do 
    let!(:users) { 
    [:users_attribute_a, :users_attribute_b, :users_attribute_c].map do |a| 
     Factory(:user, => 'invalid_value') 
    end 
    } 

    context "Given a user" do 
    context "With an invalid value" do 
     subject { users } 
     it { subject.all?{|user| should_not be_valid } 
    end 
    end 
end