How can I test common Rails controller behavior while staying DRY?
I've been writing RSpec tests for some Rails controllers and I've discovered a strong impulse to ensure that the Authlogic authentication is working properly. I also feel like I should be verifying that each action uses the same application-wide layout. However, writing tests for this behavior in every single action seems silly.
What I'd like to see are one-line matchers for filters and layouts, similar to Shoulda's matchers for associations and verifications. Unfortunately, no such matchers seem to be available (except for some Test::Unit macros for filters in this blog post). I'm tempted to just write them myself, but no开发者_StackOverflow中文版t being able to find anyone who's already done it makes me question whether or not a need for such matchers actually exists.
So my question is, how do you test your controllers' common behavior (if you test it at all), and would one-liner matchers testing filters and layouts be useful? Myself, I'm trying to decide between one-liners in the controller specs combined with speccing the filter explicitly, or just speccing the filter and ignoring the filters and layouts in the controllers (since they're only one line of code anyway).
I don't like the idea of writing specs for filters -- that seems too close to the implementation. If you had used TDD/BDD methods to build your controller from scratch, presumably you'd have written the action first, added some logic (e.g. to handle authentication) and then realized it should go into a filter instead. If your spec is along the lines of "Reject an index request if the current user is not the account user", your spec ought to be able to do something like (aircode):
current_user = Factory.create(:unauthorized)
controller.should_not receive(:index)
get :index
request.should redirect_to(some_safe_path)
And it doesn't matter whether the action is using a filter or not.
You can DRY up controller specs with Rspec macros. So (more hand-waving):
describe MyController do
should_reject_anonymous(self)
...
end
module ControllerMacros
def should_reject_anonymous(test_controller)
describe test_controller, "Authentication" do
it "rejects index" do
test_controller.should_not_receive(:index)
get :index
response.should redirect_to(some_safe_path)
end
end
end
end
精彩评论