如何在登录后检查用户是否会被重定向到他/她的个人资料(Rails Tutorial Ch.9)?

JTM*_*JTM 2 ruby-on-rails railstutorial.org

我正在研究Rails教程的第9章练习1.我不确定如何测试,在正常登录后,用户将被重定向到他/她的个人资料.练习的提示意味着我应该在users_edit_test.rb中修改"使用友好转发的成功编辑"测试,以便将session [:forwarding_url]的值与用户配置文件路径的值进行比较.为此,我补充道

assert_equal session[:forwarding_url], user_path
Run Code Online (Sandbox Code Playgroud)

到功能结束.但是,当我进行rake测试时,我收到此错误:

Expected: nil
Actual: "/users/762146111"
Run Code Online (Sandbox Code Playgroud)

任何人都可以帮我解决这个问题吗?

更新:以下是正在测试的控制器和帮助程序:

class SessionsController < ApplicationController
  def new
  end

  def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      log_in user
      params[:session][:remember_me] == '1' ? remember(user) : forget(user)
      redirect_back_or user
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end

  def destroy
    log_out if logged_in?
    redirect_to root_url
  end
end

module SessionsHelper
  # Logs in the given user.
  def log_in(user)
    session[:user_id] = user.id
  end

  # Remembers a user in a persistent session.
  def remember(user)
    user.remember
    cookies.permanent.signed[:user_id] = user.id
    cookies.permanent[:remember_token] = user.remember_token
  end

  # Returns true if the given user is the current user.
  def current_user?(user)
    user == current_user
  end

  # Returns the current logged-in user (if any).
  def current_user
    if (user_id = session[:user_id])
      @current_user ||= User.find_by(id: user_id)
    elsif (user_id = cookies.signed[:user_id])
      user = User.find_by(id: user_id)
      if user && user.authenticated?(cookies[:remember_token])
        log_in user
        @current_user = user
      end
    end
  end

  # Returns true if the user is logged in, false otherwise.
  def logged_in?
    !current_user.nil?
  end

  # Forgets a persistent session.
  def forget(user)
    user.forget
    cookies.delete(:user_id)
    cookies.delete(:remember_token)
  end

  # Logs out the current user.
  def log_out
    forget(current_user)
    session.delete(:user_id)
    @current_user = nil
  end

  # Redirects to stored located (or to the default).
  def redirect_back_or(default)
    redirect_to(session[:forwarding_url] || default)
    session.delete(:forwarding_url)
  end

  # Stores the URL trying to be accessed.
  def store_location
    session[:forwarding_url] = request.url if request.get?
  end
end
Run Code Online (Sandbox Code Playgroud)

M. *_*ton 8

我回答这个问题的时间已经很晚了,所以现在可能是一个没有实际意义的问题,但我现在正在自己的教程中进行这项练习,并且不得不努力想出正确的答案,但我终于做到了.所以我想我会分享我所做的让测试通过,以防你还在寻找答案.首先,我将指出当前逻辑中的缺陷(因为我最初犯了同样的错误,并且必须处理代码的逻辑流程以找出问题所在).

在您的测试中,您正在测试与之间的相等性assert_equal session[:forwarding_url], user_path(@user).问题是,用户user_path永远不会被设置为转发网址.在该sessions_helper.rb文件中,该redirect_back_or()方法具有以下行:( 传递给此方法redirect_to(session[:forwarding_url] || default)default参数是user在会话控制器的create动作中调用它的时候).所以redirect_back_or()方法中的这一行可以用简单的英文阅读

如果已由store_session方法设置了转发URL,则重定向到转发URL .如果转发URL为nil,则重定向到默认页面,在这种情况下是用户页面.

为了测试你正在尝试做的相等(并且正如我最初尝试的那样),它需要在某个时刻user_path(@user)或者user必须将其设置为会话的转发URL.store_session没有参数来执行此操作,因此user_path(@user)可以设置为转发URL的唯一方法是,如果未经授权的用户尝试访问该页面并且首先被强制登录然后被重定向到那里(尽我所能告诉,本文也没有要求将信息记录在人查看单个用户的页面).所以,user_path(@user)永远不会令人信服地设置为在转发URL store_session方法,正是如此,assert_equal session[:forwarding_url], user_path(@user)永远不会计算为true.

此时,您在原始问题中引用的错误消息对解决您的问题非常有帮助.

Expected: nil
Actual: "/users/762146111"
Run Code Online (Sandbox Code Playgroud)

如果您从Hartl的文本(或Rails API)的早期章节中回忆起,assert_equal测试需要两个必需的参数:期望值和实际值,然后比较两者.这反映在您的错误信息:session[:forwarding_url]计算结果为nil,而user_path(@user)计算结果为特定用户的网址.因为,正如我之前所说,用户的个人资料页面绝不应该被设置为转发网址,我们可以user_path(@user)完全从我们的测试中消除.

对我们来说幸运的是,教科书中的练习并不要求我们用户assert_equal获得正确的结果.我们只需要测试session[:forwarding_url]具有正确价值的东西.这应该很容易.让我一步一步地走过去:

  1. 用户未登录并尝试访问通过索引,编辑,更新或销毁控制器操作提供的页面.
  2. 在过滤器捕获之前并运行该logged_in_user方法.unless logged_in?计算结果为false,因此运行此方法中的代码.
  3. 内部调用store_location logged_in_user,并将未授权用户的get请求存储为转发URL.同时,用户被重定向到登录页面.
  4. 假设用户输入有效凭据以成功登录,则会调用会话控制器的create方法来创建新会话.此操作将用户登录并调用redirect_back_or().
  5. redirect_back_or()检查是否session[:forwarding_url]有与之关联的值 - 我们知道这是因为store_location在步骤#3中为此键分配了一个值.所以上半部分redirect_back_or()是|| 语句被执行,现在经过身份验证的用户会在登录之前被重定向到他们最初尝试访问的页面.
  6. redirect_back_or() 然后删除与转发URL关联的值,因为用户已经重定向到那里.
  7. 现在我们尝试再次登录,这次是直接访问登录页面.这成功发生,并再次调用会话控制器的创建操作.
  8. 这一次,我们没有尝试访问我们没有访问权限的页面,因此store_session方法永远不会被调用.因此,forwarding_url目前为零.
  9. 当会话被创建redirect_back_or()并被调用时,session[:forwarding_url]求值为nil,所以||的后半部分 语句被执行,即redirect_to user(因为user传递给方法的参数是默认值).

总而言之,后续登录尝试的正确值session[:forwarding_url]应为零.您可以通过以下两种方式之一编写此测试:

  1. 您仍然可以使用它assert_equal来测试相等性:

    assert_equal session[:forwarding_url], nil

  2. 您可以将您的测试重构为更简洁和直接:

    assert_nil session[:forwarding_url]

我更喜欢测试#2,但我对它们进行了测试,并且两种方式都很好.

虽然在原始练习中没有明确要求,但我还在此之后添加了一个测试,以确保所有后续重定向都发送到user_path(@user):

assert_nil session[:forwarding_url]
assert_redirected_to @user



TL; DR: session[:forwarding_url]永远不会平等user_path(@user).如果它尚未存储store_location(所有后续登录尝试都是这种情况),它将评估为nil,因此必须使用以下两种方法之一来编写此练习的通过测试:

assert_nil session[:forwarding_url]
要么
assert_equal session[:forwarding_url], nil