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.

Reply via email to