Reputation: 355
I'm trying to restrict a few controllers in my app to require a login before any action. I know how to implement it, but not how to write nice tests in rspec for it.
For example, if I want to restrict every action of my users controller to require login, I could have a test like:
describe "authorization" do
describe "for non-signed-in users" do
let(:user) { FactoryGirl.create(:user) }
describe "in the Users controller" do
describe "visiting the index page" do
before { visit users_path }
it { should have_selector('title', text: 'Log In') }
end
describe "visiting the edit page" do
before { visit edit_user_path(user) }
it { should have_selector('title', text: 'Log In') }
end
describe "submitting to the update action" do
before { put user_path(user) }
specify { response.should redirect_to(login_path) }
end
describe "submitting a DELETE request to the Users#destroy action" do
before { delete user_path(user) }
specify { response.should redirect_to(root_path) }
end
....etc.....
end
end
Do I need to specify all 7 of my restful routes for every controller I want to test? Seems pretty inefficient. Is there some way to say "before visit any users routes response should redirect to login_path"?
Upvotes: 2
Views: 2440
Reputation: 694
I had a similar concern while trying to list and then test all my application routes for 500 errors, and I didn't want to manually add each route (I have 150~ or so) one by one.
I mirrored the code inside the command rake routes
, and used ActionDispatch::Routing::RouteWrapper
as well as Rails.application.routes.routes
to list them.
The wrapper provides an easy way to check what is the controller, verb, and action of a route. From there you just have to filter the routes you want to check and iterate over them while running your test on each one.
context 'all routes' do
let(:all_app_routes) do
Rails.application.routes.routes.collect do |route|
ActionDispatch::Routing::RouteWrapper.new route
end.reject(&:internal?)
end
context 'in the Users controller' do
let(:users_controller_routes) do
all_app_routes.select { |route| route.controller == 'users' }
end
it 'all routes should redirect to login' do
users_controller_routes.each do |route|
begin
# reconstruct the path with the route name
# I did not test the line below, I personnaly kept using get('/' << route.name) as my case was to test the index pages only.
# but you get the idea: call your http route below (http.rb, net/http, ...)
send(route.verb, '/' << route.name)
# will produce something like : get('/users/')
# test it does indeed redirect
expect(response.status).to eq(302)
expect(response.body).to include?('my_redirect_location')
# you could also continue further testing
follow_redirect!
expect(response.body).to include('<div id="login">')
rescue Exception
next
# or fail test depending on what you want to check
# I had the case of abstract method in controllers that raised exception
end
end
end
end
end
I personnaly used this code only to test index methods (select {|route| route.action =='index' }...
), as bulk testing create/destroy/new/edit proved to be too difficult (differents required params every time)
Upvotes: 2