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