FactoryGirl:为什么attributes_for省略了一些属性?

fea*_*ool 16 ruby-on-rails-3 factory-bot

我想在控制器测试中使用FactoryGirl.attributes_for,如:

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
Run Code Online (Sandbox Code Playgroud)

...但这不起作用,因为#attributes_for省略了:user_id属性.这里的区别#create#attributes_for:

>> 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}
Run Code Online (Sandbox Code Playgroud)

请注意:不存在:user_id #attributes_for.这是预期的行为吗?

FWIW,我的工厂文件包含的定义:premise_group:user:

FactoryGirl.define do
  ...
  factory :premise_group do
    sequence(:name) {|n| "PremiseGroup_#{n}"}
    user
    is_visible false
    is_open false
  end
  factory :user do
    ...
  end
end
Run Code Online (Sandbox Code Playgroud)

fea*_*ool 26

深入研究FactoryGirl文档,例如这个wiki页面,你会发现attribues_for忽略关联的提及.我们不想知道为什么(但我确实提交了一个问题)(但请参阅下面的更新).作为一种变通方法,我裹着一个辅助方法围绕build_attributes这条attributes_for,FactoryGirl.build(...).attributesid:

def build_attributes(*args)
  FactoryGirl.build(*args).attributes.delete_if do |k, v| 
    ["id", "created_at", "updated_at"].member?(k)
  end
end
Run Code Online (Sandbox Code Playgroud)

所以现在:

>> build_attributes(:premise_group)
=> {"name"=>"PremiseGroup_21", "user_id"=>29, "is_visible"=>false, "is_open"=>false}
Run Code Online (Sandbox Code Playgroud)

......这正是预期的结果.

更新

吸收了FactoryGirl创建者的评论后,我理解为什么created_at忽略关联:引用关联会生成对db的调用,这会在某些情况下大大减慢测试速度.但如果您需要关联,updated_at上面显示的方法应该有效.

  • @MikeHan:将#build_attributes放在spec/spec_helper.rb中应该没问题.在我的例子中,我把它放在spec/factories.rb文件中.将它放在spec/factory_girl_helper.rb中可能会稍微清些,并在需要时包含它. (3认同)