FactoryGirl: attributes_for not giving me associat

2020-02-02 15:29发布

问题:

I have a Code model factory like this:

Factory.define :code do |f|
    f.value "code"
    f.association :code_type
    f.association(:codeable, :factory => :portfolio)
end

But when I test my controller with a simple test_should_create_code like this:

  test "should create code" do
    assert_difference('Code.count') do
      post :create, :code => Factory.attributes_for(:code)
    end
    assert_redirected_to code_path(assigns(:code))
  end

... the test fails. The new record is not created.

In the console, it seems that attributes_for does not return all required attributes like the create does.

rob@compy:~/dev/my_rails_app$ rails console test
Loading test environment (Rails 3.0.3)
irb(main):001:0> Factory.create(:code)
=> #<Code id: 1, code_type_id: 1, value: "code", codeable_id: 1, codeable_type: "Portfolio", created_at: "2011-02-24 10:42:20", updated_at: "2011-02-24 10:42:20">
irb(main):002:0> Factory.attributes_for(:code)
=> {:value=>"code"}

Any ideas?

Thanks,

回答1:

You can try something like this:

(Factory.build :code).attributes.symbolize_keys 

Check this: http://groups.google.com/group/factory_girl/browse_thread/thread/a95071d66d97987e)



回答2:

This one doesn't return timestamps etc., only attributes that are accessible for mass assignment:

(FactoryGirl.build :position).attributes.symbolize_keys.reject { |key, value| !Position.attr_accessible[:default].collect { |attribute| attribute.to_sym }.include?(key) }

Still, it's quite ugly. I think FactoryGirl should provide something like this out of the box.

I opened a request for this here.



回答3:

I'd suggest yet an other approach, which I think is clearer:

attr = attributes_for(:code).merge(code_type: create(:code_type))


回答4:

heres what I end up doing...

conf = FactoryGirl.build(:conference)
post :create, {:conference => conf.attributes.slice(*conf.class.accessible_attributes) }


回答5:

I've synthesized what others have said, in case it helps anyone else. To be consistent with the version of FactoryGirl in question, I've used Factory.build() instead of FactoryGirl.build(). Update as necessary.

def build_attributes_for(*args)
  build_object = Factory.build(*args)
  build_object.attributes.slice(*build_object.class.accessible_attributes).symbolize_keys
end

Simply call this method in place of Factory.attributes_for:

post :create, :code => build_attributes_for(:code)

The full gist (within a helper module) is here: https://gist.github.com/jlberglund/5207078



回答6:

In my APP/spec/controllers/pages_controllers_spec.rb I set:

let(:valid_attributes) { FactoryGirl.attributes_for(:page).merge(subject: FactoryGirl.create(:theme), user: FactoryGirl.create(:user)) } 

Because I have two models associated. This works too:

 FactoryGirl.define do
    factory :page do
       title      { Faker::Lorem.characters 12 }
       body       { Faker::Lorem.characters 38 }
       discution  false
       published  true
       tags       "linux, education, elearning"
       section   { FactoryGirl.create(:section) }
       user      { FactoryGirl.create(:user)    }                                                                                                                            
     end
  end


回答7:

Here's another way. You probably want to omit the id, created_at and updated_at attributes.

FactoryGirl.build(:car).attributes.except('id', 'created_at', 'updated_at').symbolize_keys

Limitations:

  • It does not generate attributes for HMT and HABTM associations (as these associations are stored in a join table, not an actual attribute).
  • Association strategy in the factory must be create, as in association :user, strategy: :create. This strategy can make your factory very slow if you don't use it wisely.