我按照《Ruby on Rails 教程》一书,使用 cookies 完成记住用户的功能,但是之后进行集成测试时发生了如下错误:
signed 方法找不到
ERROR UsersLoginTest#test_login_with_valid_information_followed_by_logout (3.96s)
Minitest::UnexpectedError: NoMethodError: undefined method `signed' for #<Rack::Test::CookieJar:0x00007f6e07adc6f0
......
app/helpers/sessions_helper.rb:14:in `current_user'
app/helpers/sessions_helper.rb:25:in `logged_in?'
test/integration/users_login_test.rb:38:in `block in <class:UsersLoginTest>'
相关内容如下:
class UsersLoginTest < ActionDispatch::IntegrationTest
...
test "login with valid information followed by logout" do
get login_path
post login_path, params: { session: { email: @user.email, password: "password" } }
assert_redirected_to @user # 检查重定向地址是否正确
follow_redirect! # 访问重定向地址
assert_select "a[href=?]", login_path, count: 0
assert_select "a[href=?]", logout_path
assert_select "a[href=?]", user_path(@user)
delete logout_path
assert_not logged_in?
assert_redirected_to root_url
follow_redirect!
assert_select "a[href=?]", login_path
assert_select "a[href=?]", logout_path, count: 0
assert_select "a[href=?]", user_path(@user), count: 0
end
end
module SessionsHelper
# 登录指定用户
def log_in(user)
# 利用Rails提供的session方法在浏览器中创建一个临时cookie,内容是加密后的用户ID
# 后续请求中,可以使用session[:user_id]取回这个ID
session[:user_id] = user.id
end
# 返回当前登录的用户(如果有的话)
def current_user
# 如果session[:user_id]中有用户ID,就使用其检索用户
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id]) # 否则检查cookies[:user_id],并检索登录持久会话中存储的用户
user = User.find_by(id: user_id)
if user && user.authenticated?(cookies[:remember_token])
log_in user # 将用户ID存入session
@current_user = user
end
end
end
# 判断用户是否已经登录,已登录返回true,否则返回false
def logged_in?
!current_user.nil?
end
# 退出当前用户
def log_out
forget(current_user)
session.delete(:user_id) # 从会话中删除用户ID
@current_user = nil? # 可选步骤:将当前用户设为nil
end
# 在持久会话中记住用户
def remember(user)
user.remember # 为用户生成一个记忆令牌,并将其摘要记录到数据库记录中
# permanent方法用于设置当前cookie的过期时间为20年后,signed方法用于对cookie值进行签名和加密
cookies.permanent.signed[:user_id] = user.id # 对用户ID进行签名和加密
cookies.permanent[:remember_token] = user.remember_token
end
# 忘记持久会话
def forget(user)
user.forget
# 删除cookie
cookies.delete(:user_id)
cookies.delete(:remember_token)
end
end
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
respond_to do |format|
if user && user.authenticate(params[:session][:password])
# 保存用户登录信息
log_in user
remember user
# 用户登录成功,重定向到用户资料页面
format.html { redirect_to user }
else
# flash闪现消息在一个请求的生命周期内是持续存在的,但是重新渲染页面不算一次新的请求
# 因为使用flash.now代替(flash.now专门用于在重新渲染的页面中显示闪现消息)
flash.now[:danger] = "Log in failed, please check your email and password!"
format.html { render :new, status: :bad_request }
end
end
end
def destroy
log_out
redirect_to root_url
end
end
我在stackoverflow上看到有一个类似的问题,但是解决方案对我不起作用。同时我翻了一下Rails issues列表,找到了一个类似的问题Signed cookies not available in controller tests看了一遍,也没找到合适解决的方案,说是集成测试不能访问加密后的 cookies,我点搞不懂了。
有大佬知道怎么解决上面这个问题么?谢谢