How to log in/out users with Devise in Rails4 test

2019-05-07 13:01发布

问题:

I'm trying to ensure proper user access is maintained with Devise in Rails 4, and I'm having a hard time logging a user in in the test suite.

The simplest case:

require 'test_helper'
  include Devise::TestHelpers

class SiteLayoutTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:test1)
  end

  test "logged in should get index" do
    sign_in @user
    get users_path
    assert_response :success
    assert_select "title", "User Index"
  end
end

So far I've not done more really than just implement Devise and a Users controller with the appropriate actions.

I consistently get: NoMethodError: undefined method 'env' for nil:NilClass, referring specifically to the line containing sign_in @user and I can find other instances of people getting the same error, but never seem to find an actual solution to the problem I'm attempting to solve.

How do I log a user in with Devise in Rails 4 for testing purposes? Thanks.

EDIT:

fixtures/users.yml

test1:
  id: '1'
  email: 'test1@example.com'
  encrypted_password: <%= Devise::Encryptor.digest(User, "password") %>
  created_at: <%= Time.now - 6.minutes %>
  updated_at: <%= Time.now - 4.minutes %>

SOLUTION IN SITU:

test "logged in should get index" do
  post user_session_path, 'user[email]' => @user.email, 'user[password]' =>  'password'
  get users_path
  assert_response :success
  assert_select "title", "User Index"
end

回答1:

This is from their docs:

"Do not use Devise::TestHelpers in integration tests."

You have to sign in manually. This is an example of a test for a website that does not allow users to get to the root path unless signed in. You can create a method in a support file that signs in the user manually and then call it whenever you want to sign in the user, so that you don't have to use this code every time you need to sign in a user.

require 'test_helper'

 class UserFlowsTest < ActionDispatch::IntegrationTest
   test "signed in user is redirected to root_path" do
     get user_session_path
     assert_equal 200, status
     @david = User.create(email: "david@mail.com", password: Devise::Encryptor.digest(User, "helloworld"))
     post user_session_path, 'user[email]' => @david.email, 'user[password]' =>  @david.password
     follow_redirect!
     assert_equal 200, status
     assert_equal "/", path
   end

   test "user is redirected to sign in page when visiting home page" do
     get "/"
     assert_equal 302, status
     follow_redirect!
     assert_equal "/users/sign_in", path
     assert_equal 200, status
   end
 end

EDIT: Just in case it's helpful in the future. You can use Warden Test Helpers for integration tests but the way above is a better test. This is a working example:

require 'test_helper'
include Warden::Test::Helpers
class UserFlowsTest < ActionDispatch::IntegrationTest
  test "user can see home page after login" do
    @david = User.create(email: "david@mail.com", password: Devise::Encryptor.digest(User, "helloworld"))
    login_as(@david)
    get "/"
    assert_equal 200, status # User gets root_path because he loged in
    assert_equal "/", path
    logout
    get "/"
    assert_equal 302, status # User is redirected because he loged out
    Warden.test_reset! #reset Warden after each example
  end
end