显然Rails已经弃用了ControllerTest,转而使用IntegrationTests。我想基于登录用户测试行为。我不能直接设置当前用户,必须先使用应用的登录流程,然后才能测试感兴趣的路径和行为。这很好,但是在负责处理登录和设置会话中细节的控制器完成任务后,会话似乎消失了!
作为一个简单的测试,我有一个带有WelcomeController的基本Rails应用程序,它的显示视图将根据用户的当前状态(确定会话是否设置了user_id以及该ID是否对应于DB中的记录)显示“Logged In”或“Logged Out”。
登录系统由OmniAuth处理,这很容易被模仿,我创建了一个OmniAuth处理程序返回的用户,登录详细信息由SessionsController
处理,它将显示一个表单,该表单通过邮寄方式将电子邮件和密码发送到SessionsController.create
(此登录操作的路径是/auth/identity/callback
)。
# Highly simplified logic for the login handler
class SessionsController < ApplicationController
def create
auth_hash = request.env['omniauth.auth']
user = User.find_with_omniauth(auth_hash)
session[:user_id] = user.id
Rails.logger.debug(session.inspect)
redirect_to root_path
end
end
class WelcomeController < ApplicationController
def show
Rails.logger.debug(session.inspect)
end
end
class ApplicationController < ActionController::Base
def current_user
@current_user ||= if session.key?(:user_id)
User.find_by(id: session[:user_id])
else
nil
end
end
def logged_in?
current_user.present?
end
end
现在进行测试:
require 'test_helper'
class WelcomeControllerTest < ActionDispatch::IntegrationTest
include FactoryBot::Syntax::Methods
setup do
OmniAuth.config.test_mode = true
end
teardown do
OmniAuth.config.test_mode = false
OmniAuth.config.mock_auth[:identity] = nil
end
def manual_identity_log_in(user_with_identity)
OmniAuth.config.mock_auth[:identity] = OmniAuth::AuthHash.new(
{
provider: 'identity',
uid: user_with_identity.id,
info: {
email: user_with_identity.email,
first_name: user_with_identity.first_name,
last_name: user_with_identity.last_name,
name: user_with_identity.full_name
}
})
post '/auth/identity/callback', params: {email: user_with_identity.email, password: ''}
end
test "can see the landing page for logged out users" do
get "/"
assert_select( "h1", "Logged Out")
end
test "can see the welcome page for logged in users" do
current_user = create(:user_with_identity)
manual_identity_log_in(current_user)
get "/"
assert_select( "h1", "Logged In")
end
end
测试失败-始终显示“已注销”消息。
注:如果我在SessionsController.create
结束时将会话打印到日志中,我就可以看到我所期望的一切-登录已经工作并且设置完毕。但是在WelcomeController
中记录的会话示例(由测试中的后续get "/"
调用触发)对我来说,这显然是一个失败,因为会话没有在两个请求之间持久化。在上下文中,此测试是否始终确定用户要注销。
尽管进行了无休止的搜索和阅读,但在我看来,我一定错过了一些非常明显的东西,因为这是IntegrationTests的预期方法。
短暂性脑缺血发作
1条答案
按热度按时间6yjfywim1#
我已经找到了解决方案,它与会话存储配置有关。这可能有点“明白了”
当开发Rails应用程序并在开发模式下运行时,我仍然使用自签名证书以便在https上运行localhost。当然,当应用程序部署到staging和production时,它也是通过https运行的。因此,我有一个通用的初始化器,用于所有只为https设置会话安全的环境:
但是测试 * 没有使用https*,所以这个cookie配置根本不会通过cookie持久化会话。
我的解决方案是将测试模式的
secure
设置为false
:现在,会话跨两个单独的请求持久化,测试完成。