Rails - 失去与集成测试和 Capybara 的会话 - CSRF 相关?
Posted
技术标签:
【中文标题】Rails - 失去与集成测试和 Capybara 的会话 - CSRF 相关?【英文标题】:Rails - Losing session with Integration Tests and Capybara - CSRF related? 【发布时间】:2011-09-30 03:02:44 【问题描述】:我正在使用 Rails 3.1.0.rc4,我正在使用 capybara 的新 Steak-like DSL 和 Rspec 进行集成测试(使用 Devise 身份验证)
我遇到的问题是,当我运行集成测试时,来自 capybara 的机架测试驱动程序似乎完全失去了用户的登录会话,实际上,会话似乎完全清除了。
经过几天的调试,我完全不知道为什么。逐行遍历中间件堆栈,我相信我已经将问题归结为导致此问题的 ActiveRecord::SessionStore
中发生的事情。我读过here,如果 Rails 无法验证 CSRF 令牌,它将清除会话,这让我相信我配置错误,并且由于某种原因,这个测试没有验证 CSRF令牌正确。
这是 /initializers 目录中 session_store.rb 中的内容:
MyApp::Application.config.session_store :active_record_store
是否有任何了解 Rails 中的 CSRF 保护的人对为什么会发生这种情况有任何线索?
另外,还有一些注意事项:
我要测试的东西实际上可以在浏览器本身中运行,只有这一项测试会丢弃会话 将操作 url 指向另一台服务器的表单提交到另一台服务器后,会话似乎被丢弃。我在测试中使用 VCR gem 来捕获对这个外部服务器的请求/响应,虽然我相信我已经裁定外部请求是问题,但这可能与 CSRF 令牌未进行身份验证直接有关,从而清除会话。 其他涉及登录/使用会话的测试不会丢弃会话谁能告诉我这里到底发生了什么,以及为什么一个测试似乎随意放弃了它的会话并在我身上失败了?我做了很多调试,并尝试了我能想到的一切。
【问题讨论】:
我也有同样的问题...啊... 【参考方案1】:我也是水豚新手,遇到了类似的问题。
我试图登录一个用户,做这样的事情:
post user_session_path, :user => :email => user.email, :password => 'superpassword'
在我尝试对 capybara 做一些事情之前,这一切正常,例如访问一个页面并测试用户是否已登录。这个简单的测试没有通过:
visit root_path
page.should have_content("logout") #if the user is logged in then the logout link should be present
起初我以为水豚正在清除会话,但我错了。我花了一些时间才意识到驱动程序 capybara 正在使用处理自己的会话,因此,从 capybara 的角度来看,我的用户从未登录过。为此,您必须这样做
page.driver.post user_session_path, :user => :email => user.email, :password => 'superpassword'
不确定这是否是您的情况,但希望对您有所帮助。
【讨论】:
谢谢,阿里拉!也为我工作。不错的侦探。 似乎在后来的水豚中停止了工作。我有类似 thsi 的东西,并且对 pre-1 版本不太满意。现在我升级到 2.1 它以这种方式失败。鉴于此答案的日期,它很可能自 capybara 1 以来一直以这种方式工作。很好的解释!【参考方案2】:手动的方式很简单:
it "does something after login" do
password = "secretpass"
user = Factory(:user, :password => password)
visit login_path
fill_in "Email", :with => user.email
fill_in "Password", :with => password
click_button "Log in"
visit # somewhere else and do something
end
然后您可以将其分解为“spec_helper.rb”中的一个函数:
# at the bottom of 'spec_helper.rb'
def make_user_and_login
password = "secretpass"
@user = Factory(:user, :password => password)
visit login_path
fill_in "Email", :with => @user.email
fill_in "Password", :with => password
click_button "Log in"
end
并在您的任何测试中使用它(可能要求规范):
it "does something after login" do
make_user_and_login
# now test something that requires a logged in user
# you have access to the @user instance variable
end
【讨论】:
【参考方案3】:这可能是一个很长的机会,但我相信在click_button 'Sign in'
之后我们会陷入糟糕的状态,并在之后立即调用visit elsewhere
。
我的理论是,当我们单击按钮时,请求尚未完成,我们通过访问另一条路径将其终止。
来自 Capybara 文档:
当向 DSL 发出指令时,例如:
click_link('foo') click_link('bar') expect(page).to have_content('baz')
如果单击 foo 链接会触发异步过程,例如 Ajax 请求,该过程完成后会将 bar 链接添加到页面,单击 bar 链接会失败,因为该链接不会还存在。 但是 Capybara 足够聪明,可以在短时间内重试查找链接,然后放弃并抛出错误。
如果是这种情况,解决方案很简单:给 Capybara 寻找的东西,让它等到请求完成。这可以像添加一样简单:
expect(page).to have_text('Signed in as bob@example.com')
【讨论】:
【参考方案4】:我可以通过在 config/initializers/test.rb 中将此值设置为 true 来修复此错误
# Disable request forgery protection in test environment
config.action_controller.allow_forgery_protection = true
之前,CSRF <meta>
标签没有打印到<head>
。更改此值后,它们终于出现了。
【讨论】:
我相信 config/environments/test.rb 是预期的。以上是关于Rails - 失去与集成测试和 Capybara 的会话 - CSRF 相关?的主要内容,如果未能解决你的问题,请参考以下文章
在 Rails 中使用 Capybara 和 Selenium 测试 Google 地图标记
在 Rails 3.1 中使用 Capybara、Rspec 和 Selenium 进行测试时登录失败