簡體   English   中英

如何驗證和測試Rails 4中關聯對象的串聯創建?

[英]How do I validate and test the tandem creation of associated objects in Rails 4?

在我的應用中,初始化用戶后,我希望他們構建5個項目。 我見過斷言存在例如expect(@user.items.count).to eq(5) ..的測試。 但是,我一直在嘗試驗證項目的長度並測試驗證本身,而不是與用戶關聯的對象數。 這有可能嗎? 如果是這樣,最好的方法是什么?

這是我到目前為止的相關代碼。

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,Faker和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的行上,我收到了錯誤的nil未定義方法項。

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

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

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

創建User實例時,將init_items並創建Item實例。 但是,此時尚未定義用戶的id,因此創建的項的user_id值為nil 依次導致表的user方法在number_of_items驗證中返回nil

當刪除Item驗證時,RSpec示例將失敗,因為您正在對Item進行驗證(即user3.items.create的結果),而不是驗證結果User 相反,您可以執行以下操作:

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

我會避免使用after_initialize 每當實例化對象時都會調用它,即使僅調用User.find之后User.find 如果必須使用它,請為new_record?添加測試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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM