Unable to set session hash in Rails 5 controller test
Asked Answered
F

4

13

According to the Rails Edge Guide all ActionDispatch::IntegrationTest HTTP requests take optional named keyword arguments:

get post_url, params: { id: 12 }, session: { user_id: 5 }

Great. Now, I've got the following code in a controller test:

test 'should redirect from login page if user is logged in' do
  get '/login', session: { user_id: users(:stephen).id }
  assert_redirected_to root_url, 'Expected redirect to root'
end

And when I run it, my test fails and I see the following deprecation warning:

ActionDispatch::IntegrationTest HTTP request methods will accept only
the following keyword arguments in future Rails versions:
params, headers, env, xhr

So obviously it's rails is not letting me pass a keyword argument named session.

Furthermore, both of the old methods of setting the session in a functional test no longer work either:

test "some thing" do
  session[:user_id] = users(:stephen).id
  # etc
end

NoMethodError: undefined method `session' for nil:NilClass

And this fails too:

test "some thing" do
  get '/login', nil, nil, { user_id: users(:stephen).id }
  # etc
end

The session hash is just ignored and the deprecation warning about rails only accepting 4 different named arguments appears.

Is anyone else having this sort of trouble with Rails 5.rc1?

Frankfurter answered 13/6, 2016 at 17:49 Comment(6)
According to the example get post_url, params: { id: 12 }, session: { user_id: 5 } the second argument is params, get '/login', session: { user_id: users(:stephen).id } but you pass the session, have you tried get '/login', params: {}, session: { user_id: users(:stephen).id } ?Parton
@Зелёный the arguments are all entirely optional, and order is unimportant, and yes I did try your idea, and it doesn't fix the issueFrankfurter
That's weird, because that works smoothly in my testsParton
I'll start from scratch and see if I've done something dumb elsewhere in my code.Frankfurter
Are you absolutely sure that the error comes from the test that you pasted in the question? The error should be shown only when calling the test the old way, i.e. using positional parameters instead of hash.Analogy
@BoraMa I fixed the issue temporarily using some things I found in the link your posted. Basically there is a rails bug, and I posted an answer below. Thanks for your help.Frankfurter
C
17

In Rails 5 it is no longer possible to access session in controller tests: http://blog.napcs.com/2016/07/03/upgrading-rails-5-controller-tests/. The suggestion is to access the controller method that would set the appropriate session value for you. This comment shows and example of how you might work around this limitation: https://github.com/rails/rails/issues/23386#issuecomment-192954569

  # helper method
  def sign_in_as(name)
    post login_url, params: { sig: users(name).perishable_signature )
  end

class SomeControllerTest
  setup { sign_in_as 'david' }

  test 'the truth' do
  ..
Cioban answered 22/4, 2017 at 19:51 Comment(0)
S
2

Try set session through open_session method

open_session do |sess|
  sess.get "/login", user_id: users(:stephen).id
  assert_redirected_to root_url, 'Expected redirect to root'
end
Stedt answered 13/6, 2016 at 18:2 Comment(5)
params != sessionParton
@Frankfurter I've refreshed my answer, could you try itStedt
Thanks @Stedt it looks like there is a bug in Rails which my answer explains a bit more about.Frankfurter
This answer is incorrect for rails 5, the open_session method is no longer available: github.com/rails/rails/issues/25394Cioban
It's work is Rails 6 api.rubyonrails.org/classes/ActionDispatch/Integration/…Musing
F
1

It turns out that controller tests now inherit from ActionDispatch::IntegrationTest by default and the code that handles the behaviour I wanted sits in ActionController::TestCase.

So the fix for now is to do the following:

1 - modify your controller test to inherit from ActionController::TestCase

class SessionsControllerTest < ActionController::TestCase

2 - modify all of your http request calls to use symbolized action names instead of urls:

# so change this
get login_url

# to this
get :new

And then you should be able to use the new kw_args in your requests like so:

# now this will work fine
get :new, session: { user_id: user.id }

# and so will this
session[:user_id] = user.id

I'm going to open an issue on github later on as I imagine this behaviour is not intended. Thanks to @BoraMa for leading me to the answer

Frankfurter answered 13/6, 2016 at 19:56 Comment(0)
S
-3

I think you can do as this:

delete cart_url(@cart), params: { 'session' => { :cart_id => @cart.id }}
Strathspey answered 7/3, 2017 at 7:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.