31

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,

Robert Brown
  • 10,888
  • 7
  • 34
  • 40

7 Answers7

23

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)

Claudio Acciaresi
  • 31,951
  • 5
  • 33
  • 43
  • 7
    This has the unwanted side-effect of returning timestamps, id, etc... Not really a solution for all problems – coneybeare Feb 16 '12 at 17:05
  • 2
    @coneybeare: there's a simple fix for that. See the solution at http://stackoverflow.com/questions/10290286/factorygirl-why-does-attributes-for-omit-some-attributes – fearless_fool Oct 10 '12 at 18:19
  • Not work for me: it shows "MassAssignmentSecurity::Error: Can't mass-assign protected attributes: id, created_at, updated_at.." – Livia Huang Mar 19 '13 at 18:41
10

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.

Joshua Muheim
  • 12,617
  • 9
  • 76
  • 152
9

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

attr = attributes_for(:code).merge(code_type: create(:code_type))
harm
  • 10,045
  • 10
  • 36
  • 41
  • 2
    You can also do `attributes_for(:code).merge(code_type_attributes: attributes_for(:code_type))` – Matt Mar 23 '14 at 14:10
2

heres what I end up doing...

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

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

Jonathan
  • 21
  • 1
0

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
aarkerio
  • 2,183
  • 2
  • 20
  • 34
0

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.
BrunoF
  • 3,239
  • 26
  • 39