Max Alcala
Max Alcala

Reputation: 791

Post vs Post_via_redirect in Rails 4 not behaving in expected way

I'm stumped in a particular part of M. Hartl's Rails guide (Ch 8) with integration testing. I am trying to validate user login/logout, here's my testing code(not working):

##doesnt work
test "should display user logout behavior" do
  get login_path
  assert_template 'sessions/new'
  assert_select "a[href=?]", signup_path, count: 3
  assert_select "a[href=?]", login_path, count: 2
  post_via_redirect login_path, session: {email: @user.email, 
    password: "password"}
  assert_redirected_to @user
  assert logged_in?
  assert_select "a[href=?]", signup_path, count: 0
  assert_select "a[href=?]", login_path, count: 0
  delete logout_path
  follow_redirect!
  assert_template 'static_pages/home'
  assert_not logged_in?
  assert_select "a[href=?]", login_path, count: 2
  assert_select "a[href=?]", signup_path, count: 3
end

however, making these modifications will make it work:

#works
test "should display user logout behavior" do
  get login_path
  assert_template 'sessions/new'
  assert_select "a[href=?]", signup_path, count: 3
  assert_select "a[href=?]", login_path, count: 2
  post login_path, session: {email: @user.email, 
    password: "password"}
  assert_redirected_to @user
  follow_redirect!
  assert logged_in?
  assert_select "a[href=?]", signup_path, count: 0
  assert_select "a[href=?]", login_path, count: 0
  delete logout_path
  follow_redirect!
  assert_template 'static_pages/home'
  assert_not logged_in?
  assert_select "a[href=?]", login_path, count: 2
  assert_select "a[href=?]", signup_path, count: 3
end

The part that fails is the assert_redirected_to. I have checked via response.body and everything gets rendered as expected...

Essentially the only difference is post v. post_via_redirect. Looking at the source code of 'via_redirect', I believe they should work as all they are doing is following redirects until there are none left. In addition, I have no further redirects than those I'm testing for:

def create
    user = User.find_by email: params[:session][:email]
    if user && user.authenticate(params[:session][:password].downcase)
      log_in(user)
      redirect_to user_path(user.id)
    else
      flash.now[:danger] = "Invalid username/password combination"
      render 'new'  
    end
  end

  def destroy
    reset_session
    redirect_to root_path
  end

From that standpoint, here's the questions:

Many thanks!

Upvotes: 2

Views: 1065

Answers (2)

Max Alcala
Max Alcala

Reputation: 791

I'm posting here for completeness, but I was able to find my own answer. Here it is:

  • Do the redirection options get lost whenever we call a via_redirect, and thus testing assert_redirected_to @user fails?

Yes! Because of the way request_via_redirect works:

# File actionpack/lib/action_dispatch/testing/integration.rb, line 92
def request_via_redirect(http_method, path, parameters = nil, headers_or_env = nil)
  process(http_method, path, parameters, headers_or_env)
  follow_redirect! while redirect?
  status
end

After all the redirects are processed, you will end up with a @response object that will be compared in the following way:

# File actionpack/lib/action_dispatch/testing/assertions/response.rb, line 55
def assert_redirected_to(options = {}, message=nil)
  #....
  redirect_is       = normalize_argument_to_redirection(@response.location)
  redirect_expected = normalize_argument_to_redirection(options)

After your final redirect is process, your response_code will be 200 and your @response.location will be nil. So yes, your response headers will be lost and assert_redirected_to will be false.

  • Is there one more jump I'm making when running via_redirect? All I see the follow_redirect! doing is running get(response.location).

It depends, with your router/controller settings you may have more than one redirect. No matter the case, you will end up with a @response that has no location redirected (if you're not in an infinite redirect loop) and you won't have your headers available.

Thanks to @dre-hh for giving me the insight to start thinking about this.

Upvotes: 1

dre-hh
dre-hh

Reputation: 8044

I suppose the issue is just within

 assert_redirected_to @user

This assertions just checks the response headers of the previous action, which is fine. However if you follow the redirect the previous redirect response is already gone and you have the response after the redirect.

As this is an integration test there is no need to check every step of it. Because if the redirect fails the next assertion

assert logged_in?

will also fail.

So if using post_via_redirect just check something which applies for the next page.

Upvotes: 1

Related Questions