在我的应用中,初始化用户时,我希望他们构建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
答案 0 :(得分:0)
创建User
实例时,正在调用init_items
并正在创建Item
个实例。但是,此时未定义用户的ID,因此创建的项目的user_id
值为nil
。这反过来导致表格的user
方法在nil
验证中返回number_of_items
。
当您删除Item
验证时,那么您的RSpec示例将失败,因为您正在对Item
进行验证(即user3.items.create
的结果)而不是验证结果User
。相反,你可以做这样的事情:
user3.items.create(name: "test")
expect(user3).to_not be_valid
答案 1 :(得分: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
。根据我的经验,这比要求以相同方式构建所有新建对象更好。需要权衡的是,您现在需要在控制器中的User.build_with_items
操作中使用new
。