ruby-on-rails-3 - Rails 3.0.9 + Devise + Cucumber + Capybara 臭名昭著的 "No route matches/users/sign_out"

标签 ruby-on-rails-3 rspec devise cucumber capybara

我正在使用带有导轨 3.0.9、 cucumber 导轨 1.0.2、 capybara 1.0.0 的设计 1.4.2。我收到了 No route matches "/users/sign_out"单击注销时出错。我加了 :method => :delete在完成这个 so 问题( no-route-matches-users-sign-out-devise-rails-3 )后到 link_to 标记。

由于我用jquery替换了prototype,所以我也不得不改变

config.action_view.javascript_expansions[:defaults] = %w(jquery rails)


config.action_view.javascript_expansions[:defaults] = %w(jquery jquery_ujs)

绕过 rails.js not found 错误。

尽管通过上述更改,我能够成功注销并重定向到 root,但当我查看 FireBug 中 localhost:3000/users/sign_out 请求的响应时,它显示了相同的路由错误消息 click here to see the screenshot with notes

通过设计成功实现对 rails 3 应用程序的身份验证后,当我按照本教程 ( github.com/RailsApps/rails3-devise-rspec-cucumber/wiki/Tutorial ) 使用 Cucumber + Capybara + RSpec 添加功能和规范时,出现以下错误
When I sign in as "user@test.com/please"                              # features/step_definitions/user_steps.rb:41
Then I should be signed in                                            # features/step_definitions/user_steps.rb:49
And I sign out                                                        # features/step_definitions/user_steps.rb:53
  No route matches "/users/sign_out" (ActionController::RoutingError)
  <internal:prelude>:10:in `synchronize'
  ./features/step_definitions/user_steps.rb:55:in `/^I sign out$/'
  features/users/sign_out.feature:10:in `And I sign out'
And I should see "Signed out"                                         # features/step_definitions/web_steps.rb:105
When I return next time                                               # features/step_definitions/user_steps.rb:60
Then I should be signed out  

使用以下 step_definition 来表示“我退出”
Then /^I sign out$/ do
    visit('/users/sign_out')
end

我搜索了很多,发现这是因为 Rails 3 中不显眼的 javascript 被用于“数据方法”属性,但我也在某处读到 Capybara 确实检查了数据方法属性并相应地进行了操作。但它对我不起作用,所以关注这篇文章 Capybara attack: rack-test, lost sessions and http request methods我将步骤定义更改为以下内容:
Then /^I sign out$/ do
    rack_test_session_wrapper = Capybara.current_session.driver
    rack_test_session_wrapper.process :delete, '/users/sign_out'
end

但我得到了未定义的方法 processCapybara::RackTest::Driver (NoMethodError) .

遵循这条线索,我将上述步骤定义更改如下:
Then /^I sign out$/ do
    rack_test_session_wrapper = Capybara.current_session.driver
    rack_test_session_wrapper.delete '/users/sign_out'
end

这至少通过了“我退出”这一步,但退出后并没有重定向到主页,下一步失败:
And I should see "Signed out"                                         # features/step_definitions/web_steps.rb:105
  expected there to be content "Signed out" in "YasPiktochart\n\n  \n      Signed in as user@test.com. Not you?\n      Logout\n  \n\n    Signed in successfully.\n\n  Home\n  User: user@test.com\n\n\n\n" (RSpec::Expectations::ExpectationNotMetError)
  ./features/step_definitions/web_steps.rb:107:in `/^(?:|I )should see "([^"]*)"$/'
  features/users/sign_out.feature:11:in `And I should see "Signed out"'

毕竟,我不得不求助于在路由文件中添加“GET”方法来注销:
devise_for :users do get 'logout' => 'devise/sessions#destroy' end

修改了我的观点
<%= link_to "Logout", destroy_user_session_path, :method => :delete %>


<%= link_to "Logout", logout_path %>

并将我的步骤定义更改为以下内容:
Then /^I sign out$/ do
    visit('/logout')
end

这显然解决了所有问题,所有测试都通过了并且 firebug 在 sign_out 上没有显示任何错误。但我知道使用 'get' 请求销毁 session 不是一个好习惯,因为它是一种状态改变行为。

这可能是由于我使用的特定版本或 Rails、Devise、Cucumber-Rails 或 Capybara 吗?我想使用 Devise 的默认 sign_out 路由,而不是用 get 方法覆盖它,并且能够使用 Cucumber 和 RSpec 进行 BDD。我是使用 Cucumber+Capybara 的新手,是否存在另一种方法来发送 POST 请求而不是使用“visit('/users/sign_out')”,它只使用 GET 方法?

最佳答案

所以我发现

<%= link_to "Logout", destroy_user_session_path, :method => :delete %>

rails helper 生成以下 html
<a rel="nofollow" data-method="delete" href="/users/sign_out">Sign out</a>

和 jquery_ujs.js 有以下方法将具有 data-method="delete"属性的链接转换为表单并在运行时提交:
// Handles "data-method" on links such as:
// <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
handleMethod: function(link) {
var href = link.attr('href'),
method = link.data('method'),
csrf_token = $('meta[name=csrf-token]').attr('content'),
csrf_param = $('meta[name=csrf-param]').attr('content'),
form = $('<form method="post" action="' + href + '"></form>'),
metadata_input = '<input name="_method" value="' + method + '" type="hidden" />';
if (csrf_param !== undefined && csrf_token !== undefined) {
metadata_input += '<input name="' + csrf_param + '" value="' + csrf_token + '" type="hidden" />';
}
form.hide().append(metadata_input).appendTo('body');
form.submit();
}

Capybara helper visit('/users/sign_out') 只需单击链接并向没有此请求的任何路由的服务器发送 GET 请求。

与 link_to 助手相反,button_to 助手在页面呈现时在 html 中添加所需的表单,而不是依赖于 javascript:
<%= button_to "Logout", destroy_user_session_path, :method => :delete %>

生成以下html
<form class="button_to" action="/users/sign_out" method="post">
    <div>
        <input type="hidden" name="_method" value="delete">
        <input type="submit" value="Logout">
        <input type="hidden" name="authenticity_token" value="0Il8D+7hRcWYfl7A1MjNPenDixLYZUkMBL4OOoryeJs=">
    </div>
</form>

有了这个,我可以轻松地在我的“我注销”步骤定义中使用 Capybara 助手 click_button('Logout') 。

"link_to with a method anything other than GET is actually a bad idea, as links can be right clicked and opened in a new tab/window, and because this just copies the url (and not the method) it will break for non-get links..."

Max Will explained右键单击并在新选项卡中使用非获取数据方法打开 link_to 链接会导致链接断开。

可以在 on this link 中找到关于带有 ':method => :delete' 和 capybara 问题的 link_to 助手的一些更有用的讨论。

现在我会坚持使用没有 :method 属性的简单 link_to 助手,如果我想切换到非获取方法进行删除,我更喜欢使用 button_to。

同时我认为应该有一个相当于 Visit 的 capybara helper 来满足 data-method 属性来发送 post 请求,这样就可以避免使用基于 javascript 的驱动程序进行集成测试。或者可能已经有一个我不知道的。如果我错了,请纠正我。

关于ruby-on-rails-3 - Rails 3.0.9 + Devise + Cucumber + Capybara 臭名昭著的 "No route matches/users/sign_out",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6579695/

相关文章:

ruby-on-rails - 渴望加载多态

ruby-on-rails - 处理 undefined variable

ruby-on-rails-3 - 如何使用 RSpec 针对真实应用开发 Rails3 引擎?

ruby-on-rails - ActionController::UrlGenerationError:没有自定义路由的路由匹配

ruby-on-rails - 自定义密码恢复装置

javascript - Turbolinks 并在另一个浏览器选项卡中注销

java - 将 jar 文件放在供应商/目录中并将其添加到 git 存储库是个坏主意吗?

ruby-on-rails - simple_form select-field 验证后变成 input-field

ruby-on-rails-3 - 为什么这个规范/ Controller 测试不通过?它以前做过......(Rails,RSpec)

ruby-on-rails - 为整个命名空间设置 authenticate_user