Ruby on rails 如何验证和测试Rails 4中关联对象的串联创建?

Ruby on rails 如何验证和测试Rails 4中关联对象的串联创建?,ruby-on-rails,ruby,validation,rspec,associations,Ruby On Rails,Ruby,Validation,Rspec,Associations,在我的应用程序中,当用户初始化时,我希望他们生成5个项目。我见过断言存在的测试,例如,expect(@user.items.count).to eq(5)。但是,我一直在尝试验证项目的长度并测试验证本身,而不是与用户关联的对象的数量。这可能吗?如果是的话,最好的方法是什么 这是我到目前为止的相关代码 class User < ActiveRecord::Base ITEMS_ALLOWED = 5 has_many :items validates :items, lengt

在我的应用程序中,当用户初始化时,我希望他们生成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
...
当前测试尝试验证所创建的项。我尝试将验证移到Item类,但在尝试调用
user.items.count
的行上收到错误,未定义的方法items for 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
class项
================ 更新:项目类中没有验证时的失败消息

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
故障:
1) 用户初始化无效,项数超过5项
失败/错误:预期(user3.items.create(名称:“test”)。无效
应为无效

当您创建
用户
实例时,将调用
初始化项
,并创建
实例。但是,此时未定义用户id,因此所创建项目的
user\u id
值为
nil
。这反过来导致表的
user
方法在
number\u项验证中返回
nil

当您删除
验证时,您的RSpec示例将失败,因为您正在对
(即
user3.items.create
)进行验证,而不是验证结果的
用户
。相反,您可以这样做:

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

我会避免在初始化后使用
。只要对象被实例化,它就会被调用,即使仅仅调用
User.find
。如果必须使用它,请为
new\u记录?
添加一个测试,以便仅为新
用户添加项目

另一种方法是编写一个生成器方法来代替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
class用户

这允许您将项目初始化与用户初始化分开,以防您最终希望有一个
用户
没有项目。根据我的经验,这比要求所有新创建的对象都以相同的方式构建要好。折衷是您现在需要在控制器中的
new
操作中使用
User.build\u和\u项

谢谢Peter!您将如何实施验证?我很难弄清楚在哪里插入它。我看到的过程是:构建用户-->构建项-->保存用户-->保存项实际上,在进一步研究这个问题之后,我认为您应该能够在不绕过验证的情况下完成这项工作,但验证必须完全由用户完成。如果删除项目验证,是否仍有问题?如果是这样的话,你能发布细节吗?在原始帖子中添加了失败消息谢谢,我没有想到使用tap方法。我认为彼得指出的问题仍然存在;items.build尝试将项目绑定到用户id外键,但用户尚未保存(因此也没有验证)。使用
count
vs
size
可能会绊倒您<代码>计数
执行数据库查询,
大小
长度
不执行。
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