rspec + 工厂模型测试嵌套属性 [英] rspec + factory model testing nested attributes
问题描述
我有一个带有 rspec + factory_girl 的 rails4 应用程序.我想测试确保产品至少具有一项功能、竞争、用例和行业的验证.前 3 个必须属于产品,但行业可以独立存在.我还没有试过把工业放在测试中,因为我什至不能让工作成为前 3.
I have a rails4 app with rspec + factory_girl. I would like to test the validation that makes sure the product has at least one feature, competition, usecase and industry. The first 3 must belong to a product, but industry can exist on its own. I haven't tried to put industry in the test yet since I can't even make work the first 3.
我尝试了以下方法,在该方法中我创建了一个具有 product_feature、product_competition 和 product_usecase 的产品工厂.由于某种原因,它不起作用.
I tried the approach below in which I create a product factory that has product_feature, product_competition and product_usecase. For some reason it's not working.
我是否使用了正确的方法?如果是这样,我的代码有什么问题?
Do I use the right approach? If so, what's wrong with my code?
1) Product nested attribute validation has a valid factory
Failure/Error: expect(create(:product_with_nested_attrs)).to be_valid
ActiveRecord::RecordInvalid:
Validation failed: You have to choose at least 1 industry., You must have at least 1 product feature., You must name at least 1 competition., You must describe at least 1 usecase.
product.rb(更新)
product.rb (UPDATED)
belongs_to :user
has_many :industry_products, dependent: :destroy, inverse_of: :product
has_many :industries, through: :industry_products #industry exists without product; connected with has_many thru association
has_many :product_features, dependent: :destroy
has_many :product_competitions, dependent: :destroy
has_many :product_usecases, dependent: :destroy
accepts_nested_attributes_for :industry_products, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :product_features, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :product_competitions, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :product_usecases, reject_if: :all_blank, allow_destroy: true
#UPDATE
validate :product_features_limit #Also have it for product_usecases and product_competititons
def product_features_limit
if self.product_features.reject(&:marked_for_destruction?).count > 10
self.errors.add :base, "You can't have more than 10 features."
elsif self.product_features.reject(&:marked_for_destruction?).count < 1
self.errors.add :base, "You must have at least 1 product feature."
end
end
工厂
FactoryGirl.define do
factory :product_competititon do
competitor { Faker::Commerce.product_name }
differentiator { Faker::Lorem.paragraph }
product
end
factory :product_feature do
feature { Faker::Lorem.paragraph }
product
end
factory :product_usecase do
example { Faker::Lorem.sentence }
detail { Fakert::Lorem.paragraph }
product
end
factory :product do
name { Faker::Commerce.product_name }
company { Faker::Company.name }
website { 'https://example.com' }
oneliner { Faker::Lorem.sentence }
description { Faker::Lorem.paragraph }
user
factory :product_with_nested_attrs do
transient do
nested_attrs_count 1
end
after(:create) do |product, evaluator|
create_list(:product_feature, evaluator.nested_attrs_count, product: product)
create_list(:product_competititon, evaluator.nested_attrs_count, product: product)
create_list(:product_usecase, evaluator.nested_attrs_count, product: product)
end
end
end
end
product_spec.rb
product_spec.rb
RSpec.describe Product, type: :model do
describe "nested attribute validation" do
it "has a valid factory" do
expect(create(:product_with_nested_attrs).to be_valid
end
end
end
推荐答案
有一个 gem shoulda
(https://github.com/thoughtbot/shoulda),它让您可以直接使用一个匹配器测试 accepts_nested_attributes_for
(验证和关联).但是,如果您更喜欢测试行为(什么)而不是实现(如何),您可以执行以下操作...
There is a gem shoulda
(https://github.com/thoughtbot/shoulda) which let you test accepts_nested_attributes_for
(validations and associations as well) directly with one single matcher. But if you prefer to test the behavior(what) rather then implementation(how), you can do something like follows ...
正如我之前提到的(使用 factory_girl 设置模型测试对象),我会从工厂中删除关联首先.
Just as what I mentioned before (setting up objects for model testing with factory_girl), I would remove associations from factories first.
工厂
factory :product_competititon do
competitor { Faker::Commerce.product_name }
differentiator { Faker::Lorem.paragraph }
end
factory :product_feature do
feature { Faker::Lorem.paragraph }
end
factory :product_usecase do
example { Faker::Lorem.sentence }
detail { Fakert::Lorem.paragraph }
end
factory :product do
name { Faker::Commerce.product_name }
company { Faker::Company.name }
website { 'https://example.com' }
oneliner { Faker::Lorem.sentence }
description { Faker::Lorem.paragraph }
end
规格
RSpec.describe Product, type: :model do
describe "validation" do
let(:user) { create(:user) }
it "should be valid if a product has at least one competition, feature, usecase and industry" do
attr = attributes_for(:project).merge({
user_id: user.id,
product_competitions: [attributes_for(:project_competition)],
product_features: [attributes_for(:project_feature)],
product_usecases: [attributes_for(:project_usecase)],
product_industries: [attributes_for(:industry)],
})
expect(Product.new(attr)).to be_valid
end
it "should be invalid if no competition is given" do
attr = attributes_for(:project).merge({
user_id: user.id,
product_features: [attributes_for(:project_feature)],
product_usecases: [attributes_for(:project_usecase)],
product_industries: [attributes_for(:industry)],
})
expect(Product.new(attr)).to be_invalid
end
it "should be invalid if no feature is given" do
attr = attributes_for(:project).merge({
user_id: user.id,
product_competitions: [attributes_for(:project_competition)],
product_usecases: [attributes_for(:project_usecase)],
product_industries: [attributes_for(:industry)],
})
expect(Product.new(attr)).to be_invalid
end
# ... similar test cases for usecase and industry validation ...
end
end
这篇关于rspec + 工厂模型测试嵌套属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!