Yalon Gordon
Yalon Gordon

Reputation: 51

Accessing session in integration test

I'm a Rails beginner working through Michael Hartl's Rails Tutorial and am receiving an error I have no idea how to fix. For reference, this is for implementing the changes in listing 9.24 (https://www.railstutorial.org/book/advanced_login).

I skipped chapter 9 (since it is supposedly optional) but in Chapter 10 it asks to include the changes made in listing 9.24 so I did and my tests are still failing.

This is the error I am receiving when I run rails test

Error:
UsersEditTest#test_unsuccessful_edit:
NoMethodError: undefined method `session' for nil:NilClass
    test/test_helper.rb:18:in `log_in_as'
    test/integration/users_edit_test.rb:14:in `block in <class:UsersEditTest>'


bin/rails test test/integration/users_edit_test.rb:12

E

Error:
UsersEditTest#test_successful_edit:
NoMethodError: undefined method `session' for nil:NilClass
    test/test_helper.rb:18:in `log_in_as'
    test/integration/users_edit_test.rb:28:in `block in <class:UsersEditTest>'

The tests (in test/integration/users_edit_test.rb) that are failing are:

test "successful edit" do
    log_in_as(@user)
    get edit_user_path(@user)
... end
test "unsuccessful edit" do
     log_in_as(@user)
    get edit_user_path(@user)
... end

and here is the integration/test_helper method that is being called

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

What is especially confusing is that there is another method in the test helper that also uses sessions, and is called in user_login_test which works fine.

Any help would be greatly appreciated!!

Upvotes: 5

Views: 7652

Answers (3)

csalmeida
csalmeida

Reputation: 628

This is how I did it for Rails 6:

Added a helper to test_helper.rb:

# 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

Here's an example of a test in users_controller_test.rb:

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

  get user_url(@user)
  assert_response :success
end

Depending on how your form is structured you might need to wrap the credentials in a :sessions hash. Check if when your form is rendered on the page there's a session[email] in the name attribute:

<input type="text" name="session[email]" id="email">

If that's the case change the sign_in_as method to account for this in :params:

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

Upvotes: 1

seasalty
seasalty

Reputation: 91

For the benefit of anyone coming across this question in the future, for integration tests you'll need to define a test_helper method that posts to the session controllers create method, not modify the session specifically e.g.

class ActionDispatch::IntegrationTest
    def log_in_as(user, password: 'password')
        post login_path, params: { session: { email: user.email, password: password} }
    end
end

The reason your other session method works is because it's not assigning anything to the session hash, just checking if a value exists or not. For integration tests, you can't modify the session hash directly with permanence.

Upvotes: 8

William Dias
William Dias

Reputation: 339

Session is only available after first request in test cases. Your log_in_as(user) helper method could initiate the request that actually logs the user in so that session will be filled with user.id.

Check out the discussion in this thread:

https://github.com/rails/rails/issues/23386#issuecomment-192954569

Upvotes: 5

Related Questions