Rails - 与集成测试和Capybara失去会话 - CSRF相关?

时间:2011-07-07 03:02:59

标签: ruby-on-rails rspec devise capybara csrf

我正在使用Rails 3.1.0.rc4,我正在与capybara的新型类似DSL和Rspec(使用Devise身份验证)进行集成测试

我遇到的问题是,当我运行集成测试时,来自capybara的机架测试驱动程序似乎完全失去了用户的登录会话,事实上,会话似乎只是完全清除了。

经过几天的调试,我完全失去了原因。逐行通过中间件堆栈,我相信我已经将问题排除在导致此问题的ActiveRecord::SessionStore中。我已经读过here,如果Rails无法验证CSRF令牌,Rails会清除会话,这让我相信我的配置错误了,并且由于某种原因,这个测试没有验证正确的CSRF令牌。

这是我在/ initializers目录中的session_store.rb中的内容:

MyApp::Application.config.session_store :active_record_store

是否有人知道铁轨中的CSRF保护有任何线索可以解释为什么会这样?

此外,还有一些注意事项:

  • 我试图测试的东西实际上在浏览器中运行,只有这一个测试正在放弃会话
  • 在将操作URL提交到另一台服务器的表单后,会话似乎被删除。我正在使用VCR gem来捕获测试中对这个外部服务器的请求/响应,虽然我认为我已经将外部请求作为问题,但这可能与CSRF令牌没有进行身份验证直接相关,从而清理会议。
  • 涉及登录/使用会话的其他测试不会丢弃会话

任何人都可以给我任何关于这里发生了什么的线索,以及为什么一个测试似乎随意地放弃了它的会话而对我失败了?我做了很多调试,并尝试了我能想到的一切。

4 个答案:

答案 0 :(得分:20)

我也是水豚的新手,我遇到了类似的问题。

我试图以这样的方式登录用户:

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正在使用句柄自己的会话,因此,从水豚的角度来看,我的用户从未登录。为此,你必须这样做

page.driver.post user_session_path, :user => {:email => user.email, :password => 'superpassword'}

不确定这是否属于您的情况,但希望有所帮助。

答案 1 :(得分:7)

手动方式非常简单:

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

答案 2 :(得分:5)

我能够通过在config / initializers / test.rb中将此值设置为true来修复此错误

# Disable request forgery protection in test environment
config.action_controller.allow_forgery_protection = true

事先,CSRF <meta>代码未打印到<head>。更改此值后,它们最终会出现。

答案 3 :(得分:0)

这可能是一个很长的镜头,但我相信我们会在click_button 'Sign in'之后最终处于不良状态,并且会立即致电visit elsewhere

我的理论是,当我们点击按钮时,请求尚未完成,我们通过访问另一条路径来杀死它。

来自Capybara文档:

  

向DSL发出指令时,例如:

     

click_link('foo') click_link('bar') expect(page).to have_content('baz')

     

如果单击foo链接会触发异步进程,例如Ajax请求,当完成时会向页面添加条形链接,单击条形链接将会失败,因为该链接没有#39但是还存在。 然而,Capybara非常聪明,在放弃并抛出错误之前,会在短时间内重新找到该链接。

如果是这种情况,解决方案很简单:给Capybara寻找一些东西,让它等到请求完成。这可以像添加:

一样简单

expect(page).to have_text('Signed in as bob@example.com')