2014-01-13 46 views
0

在我的應用程序中,當用戶初始化時,我希望他們構建5個項目。我見過的測試斷言有,例如expect(@user.items.count).to eq(5)。但是,我一直在嘗試驗證項目的長度並測試驗證本身,而不是與用戶關聯的對象的數量。這甚至有可能嗎?如果是這樣,那麼解決這個問題的最好方法是什麼?如何驗證和測試Rails 4中關聯對象的串聯創建?

以下是我到目前爲止的相關代碼。

class User < ActiveRecord::Base 
    ITEMS_ALLOWED = 5 
    has_many :items 

    validates :items, length: {is: ITEMS_ALLOWED} 
    after_initialize :init_items 

    def init_items 
    ITEMS_ALLOWED.times { items.build } 
    end 
... 

我的相關測試,使用RSpec的,法克爾和FactoryGirl

describe User do 
    before :each do 
    @user = build(:user, username: "bob") 
    @user.save 
    end 

    it "is invalid with more than 5 items" do 
    user3 = build(:user) 
    user3.save 
    expect(user3.items.create(name: "test")).not_to be_valid 
    end 
end 

目前測試嘗試驗證所創建的項目。我嘗試將驗證移至Item類,但是我收到錯誤,未定義的方法項爲零,嘗試撥打user.items.count

class Item < ActiveRecord::Base 
    belongs_to :user 

    validates :number_of_items, length: {is: 5} 

    def number_of_items 
    errors.add("User must have exactly 5 items.") unless user.items.count == 5 
    end 
end 

================ 更新:失敗消息時,有在項目類中沒有驗證。

Failures: 

    1) User initialization is invalid with more than 5 items 
    Failure/Error: expect(user3.items.create(name: "test")).not_to be_valid 
     expected #<Item id: 16, name: "test", user_id: 3, photo: nil, created_at: "2014-01-14 00:24:11", updated_at: "2014-01-14 00:24:11", photo_file_name: nil, photo_content_type: nil, photo_file_size: nil, photo_updated_at: nil, description: nil> not to be valid 

回答

0

當您創建User例如,init_items被稱爲和正在創建的Item實例。但是,該用戶的ID未在此處定義,因此所創建項目的user_id值爲nil。這反過來導致表的user方法在您的number_of_items驗證中返回nil。因爲你上Item(的user3.items.create即結果)做了驗證,而不是驗證所產生的User

當您刪除Item驗證,那麼你RSpec的例子將失敗。相反,你可以做這樣的事情:

user3.items.create(name: "test") 
expect(user3).to_not be_valid 
+0

謝謝彼得!你將如何實現驗證?我很難搞清楚在哪裏插入它。我看到的過程是:構建用戶 - >構建項目 - >保存用戶 - >保存項目 –

+0

其實,在看了這些之後,我認爲你應該能夠在不繞過驗證的情況下完成它,但驗證需求絕對是在用戶。如果您刪除了項目驗證,那麼您是否仍然有問題?如果是這樣,你可以發佈細節? –

+0

將失敗消息添加到原始文章 –

0

我會盡量避免使用after_initialize。只要調用User.find,就會在實例化對象時調用它。如果您必須使用它,請爲new_record?添加測試,以便僅爲新的User添加項目。

另一種方法是編寫一個構建器方法來代替User.new。

class User < ActiveRecord::Baae 
    ITEMS_ALLOWED = 5 
    has_many :items 

    validates :items, length { is: ITEMS_ALLOWED } 

    def self.build_with_items 
    new.tap do |user| 
     user.init_items 
    end 
    end 

    def init_items 
    ITEMS_ALLOWED.times { items.build } 
    end 
end 

describe User do 
    context "when built without items" do 
    let(:user) { User.new } 

    it "is invalid" do 
     expect(user.items.size).to eq 0 
     expect(user.valid?).to be_false 
    end 
    end 

    context "when built with items" do 
    let(:user) { User.build_with_items } 

    it "is valid" do 
     expect(user.items.size).to eq 5 
     expect(user.valid?).to be_true 
    end 
    end 
end 

這可以讓你的項目從初始化用戶初始化分開,在你最終想要有一個User沒有項目的情況下。根據我的經驗,這比要求所有新建的對象以相同的方式構建要好。權衡是你現在需要在控制器的new動作中使用User.build_with_items

+0

謝謝,我沒有想過使用tap方法。我認爲彼得指出的問題依然存在; items.build嘗試將項目綁在USER_ID外鍵,並且用戶還沒有被(無論是因此沒有驗證還)沒有被保存。 –

+0

您可能已經通過使用'count' VS'size'絆倒。 'count'確實是一個DB查詢,''size'和'length'不是。 – zetetic