Rails 3.0.9 + Devise + Cucumber + Capybara 臭名昭著的“没有路线匹配 /users/sign_out”

2024-03-25

我正在使用 devise 1.4.2 和 Rails 3.0.9、cucumber-rails 1.0.2、capybara 1.0.0。我有No route matches "/users/sign_out"当我点击注销时出现错误。我添加了:method => :delete经历这个之后到 link_to 标签所以问题(无路由匹配用户注销-devise-rails-3 https://stackoverflow.com/questions/6557311/no-route-matches-users-sign-out-devise-rails-3 ).

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

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

to

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

解决rails.js not found 错误。

尽管通过上述更改,我能够成功注销并重定向到 root,但当我在 FireBug 中查看 localhost:3000/users/sign_out 请求的响应时,它显示相同的路由错误消息单击此处查看带有注释的屏幕截图 http://tardis1.tinygrab.com/auhm

通过设计成功实现对 Rails 3 应用程序的身份验证后,当我按照本教程使用 Cucumber + Capybara + RSpec 添加功能和规格时(github.com/RailsApps/rails3-devise-rspec-cucumber/wiki/Tutorial http://github.com/RailsApps/rails3-devise-rspec-cucumber/wiki/Tutorial),我收到以下错误

When I sign in as "[email protected] /cdn-cgi/l/email-protection/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  

使用以下步骤定义“我退出”

Then /^I sign out$/ do
    visit('/users/sign_out')
end

我搜索了很多,发现这是因为 Rails 3 中不引人注目的 javascript 被用于“数据方法”属性,但我也在某处读到 Capybara 确实检查数据方法属性并做出相应的行为。但它对我不起作用,所以按照这篇文章水豚攻击:机架测试、丢失会话和 http 请求方法 http://blog.ardes.com/2010/4/28/capybara-and-rack-test-sessions-and-http-methods我将步骤定义更改为以下:

Then /^I sign out$/ do
    rack_test_session_wrapper = Capybara.current_session.driver
    rack_test_session_wrapper.process :delete, '/users/sign_out'
end

但我得到了未定义的方法process for Capybara::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 [email protected] /cdn-cgi/l/email-protection. Not you?\n      Logout\n  \n\n    Signed in successfully.\n\n  Home\n  User: [email protected] /cdn-cgi/l/email-protection\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 %>

to

<%= link_to "Logout", logout_path %>

并将我的步骤定义更改为以下:

Then /^I sign out$/ do
    visit('/logout')
end

这显然解决了所有问题,所有测试都通过了,并且 firebug 没有在sign_out上显示任何错误。但我知道使用“get”请求来销毁会话并不是一个好的做法,因为它是一种状态更改行为。

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


所以我发现

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

Rails 助手生成以下 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>

这样我就可以在“我退出”步骤定义中轻松使用水豚助手 click_button('Logout') 。

“link_to 使用除 GET 之外的任何方法实际上是一个坏主意,因为可以右键单击链接并在新选项卡/窗口中打开,并且因为这只是复制 url(而不是方法),所以它会因非 get 而中断链接...” http://railsforum.com/viewtopic.php?pid=123023#p123023

As 马克斯·威尔解释道 http://railsforum.com/viewtopic.php?pid=123023#p123023右键单击并在新选项卡中使用非获取数据方法打开 link_to 链接会导致链接损坏。

可以找到有关 link_to helper with ':method => :delete' 和 capybara 问题的一些更有用的讨论在这个链接上 https://github.com/jnicklas/capybara/issues/12

现在,我会坚持使用不带 :method 属性的简单 link_to 帮助程序,如果我想切换到非 get 方法进行删除,我更喜欢使用 button_to 。

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Rails 3.0.9 + Devise + Cucumber + Capybara 臭名昭著的“没有路线匹配 /users/sign_out” 的相关文章

随机推荐