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@googlegroups.com.
To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.

Search Discussions

  • Peter Vandenabeele at Jan 29, 2012 at 11:28 am

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

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
grouprubyonrails-talk @
categoriesrubyonrails
postedJan 29, '12 at 9:52a
activeJan 29, '12 at 11:28a
posts2
users2
websiterubyonrails.org
irc#RubyOnRails

People

Translate

site design / logo © 2022 Grokbase