我正在使用带有rails 3.0.9,cucumber-rails 1.0.2,capybara 1.0.0的设计1.4.2。点击退出时出现No route matches "/users/sign_out"
错误。在经历了这样的问题(no-route-matches-users-sign-out-devise-rails-3)后,我将:method => :delete
添加到link_to标记。
由于我用jquery替换了原型,我也不得不改变
config.action_view.javascript_expansions[:defaults] = %w(jquery rails)
到
config.action_view.javascript_expansions[:defaults] = %w(jquery jquery_ujs)
绕过rails.js找不到错误。
虽然进行了上述更改,但我能够成功注销并重定向到root,当我在FireBug中查看localhost:3000 / users / sign_out请求的响应时,它会显示相同的路由错误消息click here to see the screenshot with notes
通过设计成功实现对rails 3 app的身份验证后,当我按照本教程(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'I退出'
Then /^I sign out$/ do
visit('/users/sign_out')
end
我搜索了很多,发现这是因为Rails 3中的unobrusive 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
但我为process
获得了未定义的方法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 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'请求来销毁会话并不是一个好习惯,因为这是一种改变状态的行为。
这可能是由于我正在使用的特定版本或Rails,Devise,Cucumber-Rails或Capybara吗?我想使用Devise的默认sign_out路由,而不是使用get方法覆盖它,并且能够使用Cucumber和RSpec进行BDD。我是新手使用Cucumber + Capybara,是否存在另一种发送POST请求的方法而不是使用“visit('/ users / sign_out')”,它只使用GET方法?
答案 0 :(得分:12)
所以我找到了
<%= 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帮助访问('/ users / sign_out')只需单击链接并向服务器发送GET请求,该服务器没有任何此请求的路由。
与link_to helper相反,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>
有了这个,我可以在我的“I退出”步骤定义中轻松使用Capybara帮助程序click_button('Logout')。
在Max Will explained右键单击并在新标签页中打开与非获取数据方法的link_to链接会导致链接断开。
关于link_to helper和':method =&gt;的更有用的讨论:删除'和capybara问题可以找到on this link
现在我会坚持使用简单的link_to helper而不使用:method属性,如果我想切换到非get方法进行删除,我宁愿使用button_to。
同时我认为应该有一个相当于访问的水豚助手,以满足发送帖子请求的数据方法属性,这样就可以避免使用基于javascript的驱动程序进行集成测试。或者可能已经有一个我不知道的。如果我错了,请纠正我。
答案 1 :(得分:8)
解决此问题的最简单方法(尽管可能不是最正确的问题)是修改路由文件以匹配应用程序的其余部分。例如。使destroy_user_session_path的GET版本工作。您可以通过修改路由文件来执行此操作,如下所示
卸下:
devise_for :users
添加:
devise_for :users do
get "/users/sign_out" => "devise/sessions#destroy", :as => :destroy_user_session
end
这有点脏。我确信Devise有理由弃用GET路线。但是,此时以任何其他方式修复它都超出了我的黄瓜知识,因为该套件中的每个测试最终都依赖于访问('/ users / logout'),这对于开箱即用的Devise来说是不可能的。路由。
<强>更新强>
您还可以通过在config / initialers / devise.rb中注释掉以下内容来解决此问题
#config.sign_out_via = :delete
答案 2 :(得分:8)
Devise 1。4。1(2011年6月27日)更改了退出请求的默认行为:
https://github.com/plataformatec/devise/commit/adb127bb3e3b334cba903db2c21710e8c41c2b40
Jose Valim解释了原因:“GET请求不应该改变服务器的状态。当注销是GET请求时,CSRF可以用来自动注销,而预加载链接的东西最终可能会错误地将你注销井“。 Cucumber希望测试GET请求而不是DELETE对destroy_user_session_path的请求。如果您打算将Cucumber与Devise一起使用,只需将此更改更改为config/initializers/devise.rb
,即可将Rails测试环境中的Devise默认值从DELETE更改为GET:
config.sign_out_via = Rails.env.test? ? :get :: delete
不要尝试调整routes.rb文件来进行修复。没有必要。如果您不打算使用Cucumber,请保留Devise的新默认值(DELETE)。
此处的示例源代码:
现在包括对Cucumber的Devise初始化程序的更改。
此处的应用程序模板:
现在检测Devise和Cucumber之间的碰撞,并根据需要改变Devise初始化程序。
使用Rails 3.1.0.rc4测试了这些更改,但行为应与Rails 3.0.9相同。如果问题未解决或您有更多信息,请在此处添加评论。
答案 3 :(得分:4)
解决此问题的正确方法在设计的维基页面中进行了解释:https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara
基本上,一旦你包含在user_step.rb文件中:
include Warden::Test::Helpers
Warden.test_mode!
您可以将visit '/users/sign_out'
替换为logout(:user)
答案 4 :(得分:0)
我实际上遇到了同样的问题但是使用了Rails / Sinatra应用程序。我已经为Rails设置了Devise并且注销工作正常。我有一个在lib中运行的GuestApp Sinatra应用程序,除了注销链接外,它运行良好。我试图在sinatra注销链接上强制data-method =“delete”,但我没有做任何事情会使请求成为删除请求。
我认为这对我来说可能是一个sinatra问题,但我认为任何进入的请求都会先由铁路线路处理,直到它们到达我的sinatra路线。我即将手动添加GET路由进行注销,但我宁愿不必这样做。
这是我的设计路线:
devise_for :member, :path => '', :path_names => {
:sign_in => "login",
:sign_out => "logout",
:sign_up => "register" }
这是我的链接:
%a{:href => '/logout', :"data-method" => 'delete', :rel => 'nofollow'}Log Out
<a href="/logout" data-method="delete" rel="nofollow">Log Out</a>
#- realized it should be method instead, but still not reaching routes.rb as delete
<a href="/logout" method="delete" rel="nofollow">Log Out</a>
答案 5 :(得分:0)
当我需要在 test.env 中使用类似的东西时:
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,long arg3) {
view.setSelected(true);
... //Anything
}
})
它为我工作,但也许这是不对的)
配置/ INIT / devise.rb
visit destroy_user_session_path