Testing accepts_nested_attributes_for with a shoulda macros

Today I was writing Shoulda tests for my models. The tests were very trivial.

should_have_one :item, :dependent => :destroy
should_have_many :properties
should_accept_neste...

As my fingers twirled hastily across the sensually-clicking macbook keyboard with an obsessive goal to produce erroneous line #3 I thought to myself “oh wait!”

There is no such macro! We have this new awesome convenience method ‘accepts_nested_attributes_for :something’ (let’s call it “anaf” for short) and no shoulda macros to test it.

Without a second thought I continued.

 ...d_attributes_for :item

Let’s write that baby.

First thing, let’s create the shoulda macro folder in your test dir if you don’t have it yet. This ‘shoulda_macros’ dir is magical as it will be detected and autoloaded by shoulda. (Thanks to this blog post for the pointer.)

~% mkdir -p test/shoulda_macros
~% mate test/shoulda_macros/accepts_nested_attributes_for_macros.rb

And now the code almighty.

module AcceptsNestedAttributesForMacros
  def should_accept_nested_attributes_for(*attr_names)
    klass = self.name.gsub(/Test$/, '').constantize
   
    context "#{klass}" do
      attr_names.each do |association_name|
        should "accept nested attrs for #{association_name}" do
          assert  klass.instance_methods.include?("#{association_name}_attributes="),
                  "#{klass} does not accept nested attributes for #{association_name}"
        end
      end
    end
  end
end

class ActiveSupport::TestCase
  extend AcceptsNestedAttributesForMacros
end

Turns out it’s as simple as squishing a Caribbean crazy ant. You just go through each supplied symbol and assert that the

"#{association_name}_attributes="

method exists in your model. Yay, and now my

should_accept_nested_attributes_for :items

will actually work!

Now I tried to do it better. I swear. I tried to make it validate options like

:allow_destroy => true

, but I can’t figure out how to do it without nasty perversions. The Rails’ anaf macro essentially class_evals a new method in which it calls another method with a hard-coded meta-parameter coming outside of closure. I’m simply not ninja enough yet to introspect to such extent.

There is however one way to do it. Hire a friendly worker from a poor country to read and assert your code for you every time you run tests. Put something like a red flashing light in their room and monkeypatch a callback into your rake task.

6 responses to “Testing accepts_nested_attributes_for with a shoulda macros”

  1. Greg Williams

    Hi, Since you have been able to get accepts_nested_attributes_for to work, I am hoping that you can tell me what I need to do. I have installed the latest activerecord gem 2.2.2, but I don’t seem to have this method. The api info says that it is in activerecord/lib/active_record/nested_attributes.rb, but I have no such file. What am I missing? Thanks. Greg

  2. Amol Hatwar

    Neat. How would pass these nested attributes as params for controller tests, say if you are using Factory Girl for stepping away from fixtures…

  3. Paul Hinze

    Ahh, just what I was looking for. Thanks for saving me the hassle of having to write this!

    You should see if you can get this upstream in shoulda with a pull request… just add it to this file:

    http://github.com/thoughtbot/shoulda/blob/7d6aa5b6ecceef0bef3399bc328275a3700e06ac/lib/shoulda/active_record/macros.rb

Leave a Reply