DEV Community

Discussion on: Authentication and Authorization à la Rails bcrypt

Collapse
cristiano profile image
cristiano • Edited on

Thanks for this guide it's really helpful and learned a lot! However could you suggest a way of writing test helpers to sign in/sign out a user?

Trying to take certain actions whilst not logged in will result in tests failing as expected. I think the challenge for me is that sessions don't seem to be supported in tests?

I have tried to make a request to sign the user in but it doesn't do it sadly:

# Helper defined in test_helper.rb
def sign_user_in
  ApplicationController.allow_forgery_protection = false
  post(sessions_url(email: "user@example.com", password: "test"))
end
Enter fullscreen mode Exit fullscreen mode

I have also tried to change the helper to authenticate the user directly but I think there's still an issue with the session:

# Helper defined in test_helper.rb
def sign_user_in
  @user = User.find_by(email: "user@example.com")
  if @user && @user.authenticate("test")
    session[:user_id] = @user.id
  end
end
Enter fullscreen mode Exit fullscreen mode

Any ideas on how to sign in the user and make sure its authorized in tests?
Thanks very much

Collapse
guledali profile image
guledali • Edited on
require "minitest/reporters"
Minitest::Reporters.use!

class ActiveSupport::TestCase
  fixtures :all


  # Log in as a particular user.
  def log_in_as(user)
    session[:user_id] = user.id
  end
end

# Below you have to monkeypatch open up the class ActionDispatch check the link below
# https://stackoverflow.com/questions/44461101/accessing-session-in-integration-test

class ActionDispatch::IntegrationTest

  # Log in as a particular user.
  def log_in_as(user, password: 'password')
    post login_path, params: { session: { email: user.email, password: password }
  end
end

Enter fullscreen mode Exit fullscreen mode

this how you would use the test-helper

class UsersEditTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:joe)
  end

  test "unsuccessful edit" do
    log_in_as(@user)          # HERE we are using the helper
    get edit_user_path(@user) # localhost:3000/users/1/edit 
    assert_response :success

    patch user_path(@user), params: { user: { name:  "",
                                              email: "invalid@invalid",
                                              password:              "password",
                                              password_confirmation: "password" } }

    assert_redirect_to edit_user_path(@user)
  end
Enter fullscreen mode Exit fullscreen mode

Also think this could be rewritten from

 if @user && @user.authenticate(params[:password])

Enter fullscreen mode Exit fullscreen mode

into

if @user.try!(:authenticate, params[:password])
Enter fullscreen mode Exit fullscreen mode
Collapse
cristiano profile image
cristiano

Thanks for providing such a detailed example guledali! 🙏

Did this run okay for you? It doesn't really work for me sadly, the test still thinks the user is not authenticated, perhaps I'm thinking about building the test the wrong way but have found examples similar to these.

Not sure what I'm missing here, but I don't think it is wrong to think that it's worth testing that a controller action can be accessed and ran when the user is logged in right? 🤔

Thread Thread
cristiano profile image
cristiano

I got it working now, the mistake I was doing was to add the credentials to a :sessions hash when passing them to :params, which isn't required because of how the form is structured:

# test_helper.rb
ENV['RAILS_ENV'] ||= 'test'
require_relative "../config/environment"
require "rails/test_help"

class ActiveSupport::TestCase
  # Run tests in parallel with specified workers
  parallelize(workers: :number_of_processors)

  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
  fixtures :all

  # Add more helper methods to be used by all tests here...
  def sign_in_as(user, password)
    post sessions_url, params: { email: user.email, password: password }
  end
end
Enter fullscreen mode Exit fullscreen mode

An example of a controller test looks like:

test "should show user" do
  sign_in_as(@user, 'password')

  get user_url(@user)
  assert_response :success
end
Enter fullscreen mode Exit fullscreen mode

The way I was doing it wrong (adding :session or :sessions):

def sign_in_as(user, password)
  post sessions_url, params: { session: {email: user.email, password: password} }
end
Enter fullscreen mode Exit fullscreen mode