Keeping Yourself DRY in Shoulda Tests
When I’m testing admin controllers, I often have tests that follow this form:
test/functional/admin/orders_controller_test.rb
1 class OrdersControllerTest < ActionController::TestCase 2 logged_in_as :active_user do 3 context "on GET to :index" do 4 setup do 5 get :index 6 end 7 8 should_deny_access 9 end 10 end 11 12 not_logged_in do 13 context "on GET to :index" do 14 setup do 15 get :index 16 end 17 18 should_deny_access 19 end 20 end 21 end
Well, this is all Ruby, right? And Ruby has wonderful blocks, and blocks can be passed around…
test/functional/admin/orders_controller_test.rb
1 class OrdersControllerTest < ActionController::TestCase 2 deny_access_tests = lambda do 3 context "on GET to :index" do 4 setup do 5 get :index 6 end 7 8 should_deny_access 9 end 10 end 11 12 logged_in_as :active_user, &deny_access_tests 13 not_logged_in, &deny_access_tests 14 end
This is valid for any block of code that you want to test again and again:
test/functional/admin/orders_controller_test.rb
1 class OrdersControllerTest < ActionController::TestCase 2 successful_index_render = lambda do 3 should_respond_with :success 4 should_render_template "new" 5 should_assign_to :orders 6 end 7 8 logged_in_as :admin do 9 context "", &successful_index_render 10 end 11 12 logged_in_as :sub_admin do 13 context "", &successful_index_render 14 end 15 end
Alternatively, and it might be easier in the end, you could use methods:
test/functional/admin/orders_controller_test.rb
1 class OrdersControllerTest < ActionController::TestCase 2 def self.should_render_successful_index_response 3 should_respond_with :success 4 should_render_template "new" 5 should_assign_to :orders 6 end 7 8 logged_in_as :admin do 9 should_render_successful_index_response 10 end 11 12 logged_in_as :sub_admin do 13 should_render_successful_index_response 14 end 15 end
Note thought that you must define your methods at the top of your test case. Remember that Ruby executes a class definition, so when you suddenly call should_render_successful_index_response, the method definition has to be available, or else Ruby will complain with a NoMethodError.
Ain’t Ruby sweet?