tommy

Cleaner RSpec Controller Specs

RSpec, dev, ruby, testing

Testing is an important part of software development. Having a solid test suite allows you develop more confidently – enable you to make large refactorings without fear of unintended consequences. I test almost all the code I write.

Recently, I found a bug that allowed users to see ‘inactive’ products; so I wrote a test to prevent that. I’ve seen lots of specs that test active products like so:

1
2
3
4
5
6
7
8
9
it "scopes products to active and available" do
    not_active = Factory(:product, :active => false)
    not_available = Factory(:product, :available => false)
    active_available = Factory(:product, :active => true, :available => true)
    get :index
    assigns(:products).should_not include(not_active)
    assigns(:products).should_not include(not_available)
    assigns(:products).should include(active_available)
end

Test like this are wordy and can lead to a ton of factoried objects. There’s got to be a better way!!

1
2
3
4
5
it "scopes products to active and available" do
    get :index
    assigns(:products).should have_scope(:active)
    assigns(:products).should have_scope(:available)
end

Ahhh how refreshing… See that have_scope method there? That matcher is accomplished by applying the scope to the set of products and asserting that it should be the same ActiveRecord::Relation. In most cases:

ActiveRecord::Relation + scope == ActiveRecord::Relation + scope + scope

so you can assert that your products relation is unchanged by adding the desired scope. This isn’t always the case though. Some orders and wheres just keep getting tacked onto the end of the relation… so it falls back to an array comparison, and that is unawesome. Anywho, the final product is:

This has helped me write legible and concise specs, mileage my vary.