Testing authentication with Sorcery and RSpec

2.6k Views Asked by At

I've spent far too long messing with this before asking for help. I can't seem to get RSpec and Sorcery to play together nicely. I've read through the docs on Integration testing with Sorcery and can post the login action properly, but my tests still doesn't think the user is logged in.

# spec/controllers/user_controller_spec
describe 'user access' do
    let (:user) { create(:user) }

    before :each do
      login_user(user[:email], user[:password])
    end

    it "should log in the user" do
      controller.should be_logged_in
    end

end

And my login_user method

# spec/support/sorcery_login
module Sorcery
  module TestHelpers
    module Rails
      def login_user email, password
        page.driver.post(sessions_path, { email: email , password: password, remember_me: false })
      end
    end
  end
end

The sessions controller handles the pages properly when I use them on the generated pages just fine. I tried outputting the results of the login_user method and it appears to properly post the data. How do I persist this logged in user through the tests? Does a before :each block not work for this? I'm just not sure where it could be running wrong and I'm pretty new to testing/RSpec so I may be missing something obvious. I'd appreciate any help.

Here's the output of the failed tests:

1) UsersController user access should log in the user
    Failure/Error: controller.should be_logged_in
    expected logged_in? to return true, got false
4

There are 4 best solutions below

1
On BEST ANSWER

I just went through this yesterday. Here's what I did, if it helps.

Sorcery provides a test helper login_user that relies on a @controller object being available. This works great in controller specs, but doesn't work in integration tests. So the workaround in integration tests is to write another method (like the one you have above) to simulate actually logging in via an HTTP request (essentially simulating submitting a form).

So my first thought is that you should try renaming your method to login_user_post or something else that doesn't collide with the built-in test helper.

Another potential gotcha is that it looks to me like the Sorcery helper assumes that your user's password is 'secret'.

Here's a link to the built-in helper so you can see what I'm talking about:

https://github.com/NoamB/sorcery/blob/master/lib/sorcery/test_helpers/rails.rb

Good luck - I really like this gem except for this part. It is really only fully explained by patching together SO posts. Here's the code I use:

Integration Helper

module Sorcery
  module TestHelpers
    module Rails
      def login_user_post(user, password)
        page.driver.post(sessions_url, { username: user, password: password}) 
      end

      def logout_user_get
        page.driver.get(logout_url)
      end
    end
  end
end

Integration Spec (where user needs to be logged in to do stuff)

  before(:each) do
    @user = create(:user)
    login_user_post(@user.username, 'secret')
  end

Controller Spec (where the regular login_user helper works fine)

  before(:each) do
    @user = create(:user)
    login_user
  end

Note that login_user doesn't need any arguments if you have an @user object with the password 'secret'.

0
On

The way you pass a password is probably wrong. It may be encrypted at this point. In provided example I will try to do this at first:

describe 'user access' do
    let (:user) { create(:user, password: 'secret') }

    before :each do
      login_user(user[:email], 'secret')
    end

    it "should log in the user" do
      controller.should be_logged_in
    end
end
0
On

Did you try adding to spec/spec_helpers.

RSpec.configure do |config|
  # ...
  config.include Sorcery::TestHelpers::Rails::Controller
end

Nota that you need to include Sorcery::TestHelpers::Rails::Controller, not just Sorcery::TestHelpers::Rails.

Then you will be able to login_user from any controller specs like:

describe CategoriesController do

  before do
    @user = FactoryGirl::create(:user)
  end

  describe "GET 'index'" do
    it "returns http success" do
      login_user
      get 'index'
      expect(response).to be_success
    end
  end
end
0
On

This seems to be very poorly documented. The above solutions did not work for me. Here's how I got it to work:

Check your sessions_url. Make sure it is correct. Also, check what params are necessary to log in. It may be email, username, etc.

module Sorcery
  module TestHelpers
    module Rails
      def login_user_post(email, password)
        page.driver.post(sessions_url, { email:email, password: password }) 
      end
    end
  end
end

RSpec config:

config.include Sorcery::TestHelpers::Rails

Spec helper:

def app
  Capybara.app
end

spec/controllers/protected_resource_spec.rb:

describe UsersController do
  before do
    # Create user

    # Login
    response = login_user_post( user.email, :admin_password )
    expect( response.headers[ 'location' ]).to eq 'http://test.host/' 
    # I test for login success here. Failure redirects to /sign_in.

    @cookie = response.headers[ 'Set-Cookie' ]
  end

  specify 'Gets protected resource' do
    get protected_resource, {}, { cookie:@cookie }
    expect( last_response.status ).to eq 200
  end