RSpec test if all models respond_to attribute

I'm trying to write an RSpec test for what seems like a relatively simple test, but it's proving pretty tricky (though I am new to RSpec).

In simple terms, I want to test that all models in my application respond_to :owner or :owners.

My first problem is looping through all of the models - how to do this in RSpec? I can normally loop through the models like:

ActiveRecord::Base.descendants.each { |model|
    model.should respond_to :owner
}

But not sure how to do this correctly in RSpec? I can't find much documentation about using loops within tests.

My second question is about an "OR" clause - how can I achieve "responds to owner or owners"? Something like:

model.should respond_to :owner || :owners

But again I can't find much documentation about using or clauses.


You can accomplish the OR condition by testing each method and rolling up the answer:

ActiveRecord::Base.descendants.each do |model|
   instance = model.new
   expected = instance.respond_to?(:owner) || instance.respond_to?(:owners)
   expected.should be_true
end

But this smells wrong to me. If the model is going to respond to only one of :owner and :owners, you should test that it responds to the correct one. Even if this means creating a spec for each model (which you should have in any case) and adding a new example, you're better off in the long run by being specific. It also helps future maintainers of your code, who would expect a model spec to be in the spec file for the model itself, not in a catch-all spec.

In this case the example is quite small—for longer ones you might consider using a shared example to DRY things up a bit.


When your running Rspec tests you are still using Ruby. So your loop should work, as long as you are in an it block. Your only problem is you have a model object not an instance of the object, so you need to create one. so one solution would be:

it "every model should respond to owners" do

  ActiveRecord::Base.descendants.each { |model|
   model.new.should respond_to :owner
  }

end

I am not sure about the or clause.