It has been about 6 months now since I started using the Shoulda testing plugin as my BDD/TDD tool of choice. Unlike a lot of other people, I did not flock to the RSpec bandwaggon. Personally I think RSpec is horribly bloated a sledgehammer for a simple issue, the need to have test code organized with nested setups and context blocks.
If you are new to Shoulda, I highly urge you to take a look at the Thoughbot project page for it. If you have the time take a look at some of their other projects, like Paperclip. These guys are really smart. If you really want to see how shoulda can sing, check out Tammer Saleh's presentation of it at the MountainWest RubyConf 2008. In that presentation he covers how you can extend Shoulda and write your own macros that generate additional tests. This is what my blog post is about.
Sample Shoulda Macro For Functional Require Login
Here is a simple module that you might find for any restful authentication system. This would be included in your main test_helper.rb file where it would add the normal login_as(user) method. The good part I want to point out is the should_require_login(*actions) macro method that shows off a neat example of how you could use Shoulda in your functional tests at the class level.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
module AuthSystem module TestHelper def self.included(receiver) receiver.extend ClassMethods receiver.send :include, InstanceMethods end module ClassMethods def should_require_login(*actions) actions.each do |action| should "Require login for '#{action}' action" do get(action) assert_redirected_to(login_url) end end end end module InstanceMethods def login_as(user) if u = users(user) @request.session[:user_id] = u.id end end end end end |
Here is how it would look in the controller functional test. These are all contrived examples, but I think it illustrates how Shoulda can be used and in general how you can make your own macros that test at a higher level.
1 2 3 4 5 6 |
class UsersControllerTest < ActionController::TestCase should_require_login :edit, :update, :etc end |









Ken Collins
Adding my own addition here. I have found that the "should_require_login" method should have a logout at the top of the method. So like this.
def should_require_login(*actions) actions.each do |action| should "Require login for '#{action}' action" do logout get(action) assert_redirected_to(login_url) end end endMy logout method looks like this:
def logout @request.reset_session end