Ruby on rails 3 工厂女孩:为什么属性_省略了一些属性?
我想使用FactoryGirl.attributes_进行控制器内测试,如:Ruby on rails 3 工厂女孩:为什么属性_省略了一些属性?,ruby-on-rails-3,factory-bot,Ruby On Rails 3,Factory Bot,我想使用FactoryGirl.attributes_进行控制器内测试,如: it "raise error creating a new PremiseGroup for this user" do expect { post :create, {:premise_group => FactoryGirl.attributes_for(:premise_group)} }.to raise_error(CanCan::AccessDenied) end 。。。但这不起作
it "raise error creating a new PremiseGroup for this user" do
expect {
post :create, {:premise_group => FactoryGirl.attributes_for(:premise_group)}
}.to raise_error(CanCan::AccessDenied)
end
。。。但这不起作用,因为#attributes_for忽略了:user_id属性。以下是的#创建
和#属性"之间的区别:
>> FactoryGirl.create(:premise_group)
=> #<PremiseGroup id: 3, name: "PremiseGroup_4", user_id: 6, is_visible: false, is_open: false)
>> FactoryGirl.attributes_for(:premise_group)
=> {:name=>"PremiseGroup_5", :is_visible=>false, :is_open=>false}
简短答复:
根据设计,FactoryGirl的属性为
故意忽略会触发数据库事务的内容,以便测试快速运行。但是如果您愿意花时间,您可以编写一个build\u attributes
方法(如下)来建模所有属性
原始答案
深入研究FactoryGirl文档,例如,您会发现提到
的attributes\u忽略了关联——请参阅下面的更新。作为一种解决方法,我围绕着FactoryGirl.build(…).attributes
包装了一个helper方法,该方法去除了id
,在处创建,以及在
处更新:
def build_attributes(*args)
FactoryGirl.build(*args).attributes.delete_if do |k, v|
["id", "created_at", "updated_at"].member?(k)
end
end
所以现在:
>> build_attributes(:premise_group)
=> {"name"=>"PremiseGroup_21", "user_id"=>29, "is_visible"=>false, "is_open"=>false}
。。。这正是人们所期望的
更新
吸收了FactoryGirl创建者的意见后,我理解了为什么attributes\u for
忽略关联:引用关联会生成对db的调用,这在某些情况下会大大降低测试速度。但是,如果您需要关联,上面显示的构建属性方法应该会起作用。我认为这比无所畏惧的傻瓜的答案稍有改进,尽管这取决于您想要的结果
用一个例子最容易解释。假设模型中有lat和long属性。在表单上,没有lat和long字段,而是lat degree、lat minute、latsecond等。这些字段稍后可以转换为十进制lat long格式
假设你的工厂是这样的:
factory :something
lat_d 12
lat_m 32
..
long_d 23
long_m 23.2
end
fearless的build\u属性将返回{lat:nil,long:nil}
。而下面的build\u属性将返回{lat\u d:12,lat\u m:32…,lat:nil…}
def build_attributes
ba = FactoryGirl.build(*args).attributes.delete_if do |k, v|
["id", "created_at", "updated_at"].member?(k)
end
af = FactoryGirl.attributes_for(*args)
ba.symbolize_keys.merge(af)
end
为了进一步阐述给定的build\u属性
解决方案,我对其进行了修改,只添加了可访问的关联:
def build_attributes(*args)
obj = FactoryGirl.build(*args)
associations = obj.class.reflect_on_all_associations(:belongs_to).map { |a| "#{a.name}_id" }
accessible = obj.class.accessible_attributes
accessible_associations = obj.attributes.delete_if do |k, v|
!associations.member?(k) or !accessible.include?(k)
end
FactoryGirl.attributes_for(*args).merge(accessible_associations.symbolize_keys)
end
还有一种方法:
FactoryGirl.build(:car).attributes.except('id'、'created\u at'、'updated\u at')。符号化\u键
限制:
- 它不会为HMT和HABTM关联生成属性(因为这些关联存储在联接表中,而不是实际的属性)
- 工厂中的关联策略必须是
create
,如关联:用户,策略::create
。如果你不明智地使用这个策略,你的工厂会变得非常缓慢
经过网络挖掘后,我认为公认的答案似乎过时了,因为它对我不起作用&特别是,我向您介绍:
Rails 5最基本功能的干净版本+
这将创建:属于关联,并将其id
(和类型
(如果:多态
)添加到属性中。它还通过FactoryBot::Syntax::Methods
包含代码
spec/support/factory\u bot\u macros.rb
模块FactoryBot::语法::方法
定义(*args)的嵌套属性
attributes=属性(用于(*args)
klass=args.first.to_.camelize.constantize
klass.反思所有协会(:所属)。每个协会|
关联=FactoryBot.create(r.class\u name.下划线)
属性[“#{r.name}_id”]=association.id
属性[“#{r.name}_type”]=association.class.name如果r.options[:多态性]
结束
属性
结束
结束
这是一个经过改编的版本,对他来说是一种荣誉。看起来有一些这样的问题:我目前将build\u attributes方法放在spec/spec\u helper.rb
下。或者我应该把它放在其他地方?@MikeHan:把#build_属性放在spec/spec_helper.rb中应该可以。在我的例子中,我把它放在spec/factories.rb文件中。把它放在spec/factory\u girl\u helper.rb中,并在需要时包含它,可能会稍微干净一些。@fearless\u傻瓜酷。谢谢你的提示。这太棒了。谢谢你的帮助!我还没有试过你的代码,但我怀疑你的意思是删除如果!成员?还有!包括对吗?
def build_attributes(*args)
obj = FactoryGirl.build(*args)
associations = obj.class.reflect_on_all_associations(:belongs_to).map { |a| "#{a.name}_id" }
accessible = obj.class.accessible_attributes
accessible_associations = obj.attributes.delete_if do |k, v|
!associations.member?(k) or !accessible.include?(k)
end
FactoryGirl.attributes_for(*args).merge(accessible_associations.symbolize_keys)
end