An easy way to write named scope tests

June 25, 2009 Pivotal Labs

The project I’m working on has a lot of named scopes which are really great. If you’re not using them already you should really try them out. Since we test drive everything we do, we needed a really easy way to write tests for all these named scopes. We came up with a little test helper method that I thought I’d share so that other people could use it.

Here’s the code:

def test_named_scope(all_objects, subset, condition)
  subset.should_not be_empty
  subset.each do |obj|
    condition.call(obj).should be_true
  end

  other_objects = all_objects - subset
  other_objects.should_not be_empty
  other_objects.each do |obj|
    condition.call(obj).should be_false
  end
end

To use it, just pass a superset of objects, the subset you want to test and then a lambda as a condition. The lambda should be true for all items in the subset and false for all the items outside of it.

It sounds complicated but it’s really easy! Here’s an example
Let’s look at a simple tag class that has a status column indicating whether the tag is on a whitelist or a blacklist. It could look like this.

class Tag < ActiveRecord::Base
   WHITELISTED = 1
   BLACKLISTED = 0
 end

We want to be able to easily grab all the whitelisted tags, so we need to add a named scope.

Here’s the spec we write first:

describe Tag do
    describe "whitelisted named_scope" do
      it "returns the whitelisted tags" do
        test_named_scope(Tag.all, Tag.whitelisted, lambda{|tag|
                                     tag.status == Tag::WHITELISTED })
      end
    end
  end
end

We run the spec, watch it fail and then go add the named scope to our Tag class.

class Tag < ActiveRecord::Base
  WHITELISTED = 1
  BLACKLISTED = 0
  named_scope :whitelisted, :conditions => {:status => WHITELISTED}
end

Then we just rerun the spec and watch it pass. Easy!

Update2: Josh Susser emailed me a really nice refactoring with the enumerable partition method and Kelly fixed a bug I introduced.

def test_named_scope(all_objects, subset, condition)
  scoped_objects, other_objects = all_objects.partition(&condition)
  scoped_objects.should_not be_empty
  other_objects.should_not be_empty
  scoped_objects.should == subset
  other_objects.should == all_objects - subset
end

About the Author

Biography

More Content by Pivotal Labs
Previous
Flickr "10+ Deploys Per Day" @ Velocity 2009
Flickr "10+ Deploys Per Day" @ Velocity 2009

My favorite talk at Velocity was by Paul Hammond and John Allspaw from Flickr, who are doing real lowercase...

Next
Jeff Hammerbacher: "Hadoop Operations", Velocity 2009 Day One
Jeff Hammerbacher: "Hadoop Operations", Velocity 2009 Day One

Jeff is Chief Scientist at Cloudera, which helps enterprises with Hadoop implementations. Hadoop consist...