views:

186

answers:

3

What's the best way to test scopes in Rails 3. In rails 2, I would do something like:

Rspec:

it 'should have a top_level scope' do
  Category.top_level.proxy_options.should == {:conditions => {:parent_id => nil}}
end

This fails in rails 3 with a "undefined method `proxy_options' for []:ActiveRecord::Relation" error.

How are people testing that a scope is specified with the correct options? I see you could examine the arel object and might be able to make some expectations on that, but I'm not sure what the best way to do it would be.

+1  A: 

Ideally your unit tests should treat models (classes) and instances thereof as black boxes. After all, it's not really the implementation you care about but the behavior of the interface.

So instead of testing that the scope is implemented in a particular way (i.e. with a particular set of conditions), try testing that it behaves correctly—that it returns instances it should and doesn't return instances it shouldn't.

describe Category do
  describe ".top_level" do
    it "should return root categories" do
      frameworks = Category.create(:name => "Frameworks")

      Category.top_level.should include(frameworks)
    end

    it "should not return child categories" do
      frameworks = Category.create(:name => "Frameworks")
      rails = Category.create(:name => "Ruby on Rails", :parent => frameworks)

      Category.top_level.should_not include(rails)
    end
  end
end

If you write your tests in this way, you'll be free to re-factor your implementations as you please without needing to modify your tests or, more importantly, without needing to worry about unknowingly breaking your application.

Ian
I've considered that, but all those "don't test rails" voices crop up and I figured all I should test was that I did declare the scope correctly. Still, doing it the way you suggest always gives me more confidence that I've got the SQL right. Thanks.
Trey Bean
That's not testing Rails at all. The fact that you've implemented it with a named scope doesn't matter. Hell, it could be a normal class method that calls `connection.select` and builds objects by hand. Whether you use Rails helpers or not doesn't make it any less *your* method. So go right ahead and test it.
Ian
A: 

FWIW, I agree with your original method (Rails 2). Creating models just for testing them makes your tests way too slow to run in continuous testing, so another approach is needed. Loving Rails 3, but definitely missing the convenience of proxy_options!

doug livesey