简体   繁体   中英

How to test Devise Sessions Controller with RSpec

I override SessionsController in my own controller and tried to test with RSpec. Fist, I setup devise with

@request.env["devise.mapping"] = Devise.mappings[:user]

my spec:

require 'rails_helper'

require 'authentication_helper'

RSpec.describe Users::SessionsController, type: :controller do include AuthenticationHelper

describe 'create new session' do

before(:each) do
  setup_auth
end

let(:user) {FactoryGirl.create(:user, username: 'john', email: 'john@gmail.com', password: 'pwd1234')}

it 'should return 200 with valid username and password' do
  post :create, user: {login: 'john', password: 'pwd1234'}

  expect(response).to have_http_status 200
  expect(controller.current_user.id).to eq(user.id)
end

end end

my SessionsController just return http 401 or http 200. When I run my spec, I get this error:

NoMethodError: undefined method authenticate?' for nil:NilClass # /usr/local/bundle/gems/devise-3.5.6/app/controllers/devise_controller.rb:103:in authenticate?' for nil:NilClass # /usr/local/bundle/gems/devise-3.5.6/app/controllers/devise_controller.rb:103:in require_no_authentication' # ./spec/controllers/users/sessions_controller_spec.rb:16:in block (3 levels) in <top (required)>' # ./spec/rails_helper.rb:45:in block (3 levels) in ' # /usr/local/bundle/gems/database_cleaner-1.6.0/lib/database_cleaner/generic/base.rb:16:in cleaning' # /usr/local/bundle/gems/database_cleaner-1.6.0/lib/database_cleaner/base.rb:98:in cleaning' # /usr/local/bundle/gems/database_cleaner-1.6.0/lib/database_cleaner/configuration.rb:86:in block (2 levels) in cleaning' # /usr/local/bundle/gems/database_cleaner-1.6.0/lib/database_cleaner/configuration.rb:87:in call' # /usr/local/bundle/gems/database_cleaner-1.6.0/lib/database_cleaner/configuration.rb:87:in cleaning' # ./spec/rails_helper.rb:44:in block (2 levels) in '

What am I doing wrong?

You know that Devise offers RSpec test helpers for controller specs. However, in request specs, they will not work.

Here is a solution for request specs, adapted from the Devise wiki. We will simply use Warden's test helpers – you probably already load them for your Cucumber tests.

First, we define sign_in and sign_out methods. These will behave just like those you know from controller specs:

module DeviseRequestSpecHelpers

include Warden::Test::Helpers

def sign_in(resource_or_scope, resource = nil)
  resource ||= resource_or_scope
  scope = Devise::Mapping.find_scope!(resource_or_scope)
  login_as(resource, scope: scope)
end

def sign_out(resource_or_scope)
  scope = Devise::Mapping.find_scope!(resource_or_scope)
  logout(scope)
end
end

Finally, load that module for request specs:

RSpec.configure do |config|
    config.include DeviseRequestSpecHelpers, type: :request
end

Done. You can now write request specs like this:

sign_in create(:user, name: 'John Doe')
visit root_path
expect(page).to include('John Doe')

Reference:
https://makandracards.com/makandra/37161-rspec-devise-how-to-sign-in-users-in-request-specs

You have to stub out the warden.authenticate! call, not just the current_user method.

For login success test cases:

before(:each) do
  # assuming `user` is defined and returns a User instance
  allow(request.env['warden']).to receive(:authenticate!).and_return(user)
  allow(controller).to receive(:current_user).and_return(user)
end

For failure cases:

before(:each) do
  allow(request.env['warden']).to receive(:authenticate!).and_throw(:warden, scope: :user)
  allow(controller).to receive(:current_user).and_return(nil)
end

This works for me in devise 4.x. Found this in https://github.com/plataformatec/devise/wiki/How-To:-Stub-authentication-in-controller-specs

You should have a helper for the sign in, for example

module AuthenticationHelpers
     module Controller
        def sign_in(user)
           controller.stub(:current_user).and_return(user)
           controller.stub(:user_id).and_return(user.id)
        end
    end

  module Feature
      def sign_in(user, options={})
          visit "/users/sign_in"
          fill_in "Email", with: user.email
          fill_in "Password", with: options[:password]
          click_button "Log in"
      end
  end
end

Any concerns, do not hesitate to comment

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM