Summary: Use stubs to return the original class for a named scope, and use expectations to specify scope requirements to simplify testing
Testing with chained named scopes that have complex attributes such as conditions can be tricky. I try to combine named scopes with clear names to communicate intent, rather than have long chains floating around in code. This can be done simply by creating a plain old method, with scoped procedures or this hack. This is a good way to keep named scopes DRY when using a specific combination across your application code. Of course you should test your named scopes.
However, consider the situation when adding a simple extension to your named scope that doesn't warrant a new named scope or combination. As an example I have the following code in a catalog application:
class Catalog ... # Returns a set of ordered page numbers based on the product page numbers def page_numbers Product.printable.find(:all, :select => "DISTINCT products.page_number"). collect {|product| product.page_number} end ... end
The printable named scope is quite complex and has been tested. I wanted to avoid duplicating test data (although factories would help here). For this method I was only concerned about a collection of products and it's page numbers, not that fact it was scoped. But it is a requirement that the products are scoped to only those that are printable.
Stubbing and setting expectations help us simplify the test and express the requirement. By stubbing the named scope and returning the Product class we can focus on creating test data for which we are concerned -- products with page numbers. The expectation "find printable products", ensures that we have scoped products as required.
class CatalogTest < ActiveSupport::TestCase context "with page numbers" do setup do Product.stubs(:printable).returns(Product) end context "generally" do setup do Catalog.page_numbers end before_should "find printable products" do Product.expects(:printable).returns(Product) end end context "when no products" do should "return an empty array" do assert_equal [], Catalog.page_numbers end end context "when multiple products" do setup do Factory(:catalog_with_page_numbers) end should "return an array of page numbers" do assert_equal [0, 1, 2, 3], Catalog.page_numbers end end end end
Although setting this expectation increases coupling to the implementation, we are justified in that it expresses the requirement in the test example.