Search code examples
ruby-on-railsrspecdevisehttp-status-code-401

Rspec for checking whether a user is signed in or not in rails using devise gem


I have used devise gem in rails to authenticate the user.I need to write test cases for checking whether a use is signed in or not using Rspec. Only user signed in will have the rights to access the posts index page.It is perfectly for signed in user in the Rspec. But for signed out user i have tried few steps but I cant get it right.I have used Warden for simple authentication for testing purpose.My post controller spec for Signed out user is :

describe "Unauthorized Access" do
    let(:user) {create(:user)}
    it "User tries to view post after Sign Out" do
      get :index
      expect(response).to have_http_status 401
    end
  end

Warden helper in the spec folder.

RSpec.shared_context "api request global before and after hooks" do
  before(:each) do
    Warden.test_mode!
  end

  after(:each) do
    Warden.test_reset!
  end
end

RSpec.shared_context "api request authentication helper methods" do
  def sign_in(user)
    login_as(user, scope: :users)
  end

  def sign_out
    logout(:users)
  end
end
When I click sign out button it is redirected to the log in page and the rails log show the log like this

Started GET "/" for 127.0.0.1 at 2018-09-05 11:33:10 +0530
Processing by PostsController#index as HTML
Completed 401 Unauthorized in 2ms (ActiveRecord: 0.0ms)


Started GET "/users/sign_in" for 127.0.0.1 at 2018-09-05 11:33:10 +0530
Processing by Devise::SessionsController#new as HTML
  Rendering devise/sessions/new.html.erb within layouts/application
  Rendered devise/shared/_links.html.erb (0.9ms)
  Rendered devise/sessions/new.html.erb within layouts/application (5.0ms)
Completed 200 OK in 69ms (Views: 67.9ms | ActiveRecord: 0.0ms)
But when I run the tests the rspec console showing error.

expected the response to have status code 401 but it was 302

  0) PostsController Unauthorized Access User tries to view post after Sign Out
     Failure/Error: expect(response).to have_http_status 401
       expected the response to have status code 401 but it was 302
     # ./spec/controllers/posts_controller_spec.rb:150:in `block (3 levels) in <top (required)>'
Rspec log

  (0.1ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
   (0.4ms)  begin transaction
Processing by PostsController#index as HTML
Completed 401 Unauthorized in 8ms (ActiveRecord: 0.0ms)
   (0.1ms)  rollback transaction


Solution

  • You should be testing in the following manner, make changes as per your requirement.

    describe PostsController do
    
      describe "Index" do
    
        it "should allow user to view the list of posts" do
          get :index, {}, sign_in(@user)
          expect(response).to redirect_to(posts_path)
        end
    
        it "should not list posts if the user is not logged in" do
          get :index
          expect(response.status).to eq(302)
          expect(response).to redirect_to(new_user_sessions_path)
        end
      end
    
    end
    

    Now what this test would do, when it runs it'll call your controller, assuming you have put authenticate_user!, it'll check if the user is logged in or not to view the list. Otherwise, the action will return 401.

    You can try the following as well

      RSpec.describe "Posts", :type => :request do
        context "Index" do
          it 'should return 401 when not logged in' do
            sign_out user
            get posts_path
            expect(response).to have_http_status(302)
            follow_redirect!
            expect(response).to have_http_status(401)
          end
        end
      end