I''m running into a bit of an issue when it comes to using factory_girl_rails (1.6.0) with 3.2''s mass_assignment_sanitizer = :strict and I''m hoping some one can offer me some guidance to improve my tests. The problem: Say you have a test that looks like this: it "allows you to create an object" do obj = FactoryGirl.build(:object) # #<Object id: nil, name: "Foo Bar", created_at: nil, updated_at: nil> post :create, :object => obj.attributes response.should be_redirect # yadda yadda yadda end The problem I''m running up against is that because FactoryGirl.build(:object) returns a built out object that includes protected attributes such as id, created_at and updated_at in the hash (this is a simplified example, I have other things in the model not listed here that are also whitelisted via attr_accessible), when I post that hash, I receive the following error: ActiveModel::MassAssignmentSecurity::Error: Can''t mass-assign protected attributes: id, created_at, updated_at The above is a simplified example. The model looks a bit more like this: class Foo < ActiveRecord::Base belongs_to :bar # a bunch of other belongs_to''s here attr_accessible :name, :bar_id, #other belongs_to ids end Factory.define :foo do name Faker::Name.name association :bar, :factory => :bar # all the other associations end I took a look through factory_girl''s docs and found the following methods for building out models: attributes_for: doesn''t go far enough because I have relations defined on my factory that need to be built and saved so their IDs can be assigned to the object upon its instantiation (or in the hash) build_stubbed: gives me random values for various fields that aren''t valid. Instead of building out the associations and saving them then assigning valid IDs, it just makes random shit up. That doesn''t quite work. create: obviously not the right choice when you''re testing a post action on a RESTful cotroller build: what I''m using now, and then just passing the attributes hash of the object. Does everything I need, except it includes attributes that are protected, thus causing the controller to flip out. Now, I can think of two (three actually) ways around this problem: 1) Go through my really big test base and manually remove any attribute that''s not accessible from the hash before doing a post. Not at all maintainable and not really a realistic option, but would allow the tests to pass. 2) Add a before_filter that sanitizes the parameters hash on create and update actions to strip out any protected attributes by looking at the model and seeing what its accessible attributes are. This seems like unnecessary code bloat, though, and kind of a clunky solution to the problem. 3) [What I really want] I need a way to get FactoryGirl to build out a hash of attributes when it''s first called to NOT include attributes that are protected or aren''t accessible, but WOULD include real object IDs for all the defined associations. That would allow me to just call post :create, :object => FactoryGirl.some_method(:object) and be done with it. Could anyone offer any insight here as to how I might go about making this work better? Obviously I could delete the setting from test.rb but that kind of defeats the purposes, and I like having extra strict security measures (especially in this particular application). Thanks! -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
Peter Vandenabeele
2012-Jan-29 11:28 UTC
Re: mass_assignment_sanitizer and Factories w/ rspec
On Sun, Jan 29, 2012 at 10:52 AM, Phoenix Rising <polarisrising-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>wrote:> I''m running into a bit of an issue when it comes to using > factory_girl_rails (1.6.0) with 3.2''s mass_assignment_sanitizer > = :strict and I''m hoping some one can offer me some guidance to > improve my tests. > > The problem: > Say you have a test that looks like this: > > it "allows you to create an object" do > obj = FactoryGirl.build(:object) # #<Object id: nil, name: "Foo > Bar", created_at: nil, updated_at: nil> > post :create, :object => obj.attributes > response.should be_redirect > # yadda yadda yadda > end > > The problem I''m running up against is that because > FactoryGirl.build(:object) returns a built out object that includes > protected attributes such as id, created_at and updated_at in the hash > (this is a simplified example, I have other things in the model not > listed here that are also whitelisted via attr_accessible), when I > post that hash, I receive the following error: > > ActiveModel::MassAssignmentSecurity::Error: > Can''t mass-assign protected attributes: id, created_at, > updated_at > > The above is a simplified example. The model looks a bit more like > this: > > class Foo < ActiveRecord::Base > belongs_to :bar > # a bunch of other belongs_to''s here > attr_accessible :name, :bar_id, #other belongs_to ids > end > > Factory.define :foo do > name Faker::Name.name > association :bar, :factory => :bar > # all the other associations > end > > I took a look through factory_girl''s docs and found the following > methods for building out models: > > attributes_for: doesn''t go far enough because I have relations defined > on my factory that need to be built and saved so their IDs can be > assigned to the object upon its instantiation (or in the hash) > > build_stubbed: gives me random values for various fields that aren''t > valid. Instead of building out the associations and saving them then > assigning valid IDs, it just makes random shit up. That doesn''t quite > work. > > create: obviously not the right choice when you''re testing a post > action on a RESTful cotroller > > build: what I''m using now, and then just passing the attributes hash > of the object. Does everything I need, except it includes attributes > that are protected, thus causing the controller to flip out. > > Now, I can think of two (three actually) ways around this problem: > > 1) Go through my really big test base and manually remove any > attribute that''s not accessible from the hash before doing a post. Not > at all maintainable and not really a realistic option, but would allow > the tests to pass. > > 2) Add a before_filter that sanitizes the parameters hash on create > and update actions to strip out any protected attributes by looking at > the model and seeing what its accessible attributes are. This seems > like unnecessary code bloat, though, and kind of a clunky solution to > the problem. > > 3) [What I really want] I need a way to get FactoryGirl to build out a > hash of attributes when it''s first called to NOT include attributes > that are protected or aren''t accessible, but WOULD include real object > IDs for all the defined associations. That would allow me to just call > post :create, :object => FactoryGirl.some_method(:object) and be done > with it. > > Could anyone offer any insight here as to how I might go about making > this work better? Obviously I could delete the setting from test.rb > but that kind of defeats the purposes, and I like having extra strict > security measures (especially in this particular application). >In the controllers tests that are auto-generated by rspec-rails, there is e.g. this kind of code: describe PeopleController do # This should return the minimal set of attributes required to create a valid # Contact. As you add validations to Contact, be sure to # update the return value of this method accordingly. def valid_attributes {} end ... describe "POST create" do describe "with valid params" do it "creates a new Person" do expect { post :create, :person => valid_attributes }.to change(Person, :count).by(1) end In that code, I replace it by default with something like this: def valid_attributes FactoryGirl.build(:full_person).attributes.delete_if{|k,v| v.nil?} end Stripping the non nil attributes from the hash solves the issue. As you show above, :id, :created_at, :update_at are (correctly) nil after a build, so this delete_if removes them from the attributes hash. * I get no warnings on the :id * I get no violations of nil created_at /updated upon update So, to centralize this pattern, this hack with a "build_attributes" seems to work: ../config/initializers$ cat generators.rb ContactApp::Application.config.generators do |g| g.helper false g.test_framework :rspec, :fixture => true, :views => false g.fixture_replacement :factory_girl, :dir => "spec/factories" end module FactoryGirl # this is a hack def self.build_attributes(*args) self.build(*args).attributes.delete_if{|k,v| v.nil?} end end And now I can do: def valid_attributes FactoryGirl.build_attributes(:full_person) end HTH, Peter -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.