On Sun, Jan 29, 2012 at 10:52 AM, Phoenix Rising <polarisris...@gmail.com>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@googlegroups.com. To unsubscribe from this group, send email to rubyonrails-talk+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.