Rails 3.0.9 + Devise + Cucumber + Capybara печально известный "Нет совпадений маршрутов / пользователи / sign_out" - PullRequest
25 голосов
/ 05 июля 2011

Я использую устройство 1.4.2 с рельсами 3.0.9, огурцы-рельсы 1.0.2, капибара 1.0.0. Я получил ошибку No route matches "/users/sign_out", когда нажал кнопку выхода. Я добавил :method => :delete в тег link_to после прохождения этого вопроса ( no-route-match-users-sign-out-devise-rails-3 ).

Поскольку я заменил прототип на jquery, мне также пришлось изменить

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

до

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

чтобы обойти rails.js не найдена ошибка.

Хотя с указанными выше изменениями я могу успешно выйти и перенаправиться в root, когда я смотрю на ответ localhost: 3000 / users / sign_out на запрос в FireBug, он показывает то же сообщение об ошибке маршрутизации нажмите здесь, чтобы увидеть скриншот с заметками

После успешной реализации аутентификации в приложении rails 3 через devise, когда я добавил функциональность и спецификации, используя Cucumber + Capybara + RSpec, следуя этому руководству ( github.com / RailsApps / rails3-devise-rspec-cucumber / wiki / Tutorial ), я получил следующую ошибку

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  

со следующим определением шага для 'Я выхожу'

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

Я много искал и обнаружил, что это связано с тем, что в Rails 3 используется неброский javascript для атрибутов «data-method», но я также где-то читал, что Capybara проверяет атрибуты data-method и ведет себя соответственно. Но у меня это не сработало, поэтому после этого поста Атака капибары: тест в стойке, потерянные сеансы и методы http-запроса Я изменил определение шага следующим образом:

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, которые я использую? Я хочу использовать маршрут по умолчанию Dev_out Devise вместо того, чтобы переопределять его методом get и иметь возможность делать BDD, используя Cucumber и RSpec. Я новичок в использовании Cucumber + Capybara. Существует ли другой метод для отправки POST-запроса вместо использования "visit ('/ users / sign_out')", который использует только метод GET?

Ответы [ 6 ]

12 голосов
/ 05 июля 2011

Итак, я обнаружил, что

<%= 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 помощник 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') в моем определении шага 'I out'.

"link_to с методом, отличным от GET, на самом деле является плохой идеей, поскольку ссылки можно щелкать правой кнопкой мыши и открывать в новой вкладке / окне, а поскольку это просто копирует URL (а не метод), его сломается за не получить ссылки ... "

Как Макс Уилл объяснил Если щелкнуть правой кнопкой мыши и открыть ссылку link_to с помощью метода non-get data в новой вкладке, получится неработающая ссылка.

Еще несколько полезных рассуждений о помощнике link_to с вопросом ': method =>: delete' и капибара можно найти по этой ссылке

На данный момент я бы использовал простой помощник link_to без атрибута: method и предпочел бы использовать button_to, если я хочу переключиться на метод non-get для удаления.

В то же время я думаю, что должен существовать помощник-капибара, эквивалентный Visit для обслуживания атрибута data-method для отправки пост-запроса, чтобы можно было избежать использования драйвера на основе javascript для тестирования интеграции. Или, может быть, там уже есть тот, о котором я не знаю. Поправь меня, если я ошибаюсь.

8 голосов
/ 10 июля 2011

Devise 1.4.1 (27 июня 2011 г.) изменил поведение по умолчанию для запросов на выход:

https://github.com/plataformatec/devise/commit/adb127bb3e3b334cba903db2c21710e8c41c2b40

Хосе Валим объяснил, почему: «GET-запросы не должны изменять состояниесервер. Когда выход - это запрос GET, CSRF может использоваться для автоматического выхода из системы, и вещи, которые предварительно загружены ссылками, могут в конечном итоге также выйти из системы по ошибке. "

Cucumber хочет проверить запросы GET, а не УДАЛИТЬзапросы на destroy_user_session_path.Если вы намерены использовать Cucumber с Devise, измените значение по умолчанию Devise с DELETE на GET для тестовой среды Rails только с этим изменением на config/initializers/devise.rb:

config.sign_out_via = Rails.env.test??: get:: delete

Не пытайтесь настроить файл rout.rb для исправления.Это не обязательно.Если вы не собираетесь использовать Cucumber, оставьте новое значение по умолчанию (DELETE) для Devise.

Пример исходного кода здесь:

https://github.com/RailsApps/rails3-devise-rspec-cucumber

теперь включает изменение инициализатора Devise для огурца.

Шаблон приложения здесь:

https://github.com/RailsApps/rails3-application-templates

теперь обнаруживает столкновениемежду Devise и Cucumber и изменяет инициализатор Devise по мере необходимости.

Эти изменения были протестированы с Rails 3.1.0.rc4, но поведение должно быть таким же с Rails 3.0.9.Пожалуйста, добавьте комментарии здесь, если проблема не решена или если у вас есть дополнительная информация.

8 голосов
/ 08 июля 2011

Самый простой способ исправить эту проблему (хотя, возможно, и не самый правильный) - изменить файл маршрутов, чтобы он соответствовал остальной части приложения. Например. заставить работать GET версию destroy_user_session_path. Вы можете сделать это, изменив файл маршрутов следующим образом

Удалить:

devise_for :users

Добавить:

devise_for :users do
  get "/users/sign_out" => "devise/sessions#destroy", :as => :destroy_user_session
end

Это немного грязно. Я уверен, что Devise отверг маршрут GET по уважительной причине. Тем не менее, исправление его любым другим способом на данный момент выходит за рамки моих знаний о Cucumber, поскольку каждый тест в этом наборе в конечном счете зависит от посещения ('/ users / logout'), что просто невозможно с помощью готового устройства Devise. маршруты.

UPDATE

Вы также можете это исправить, комментируя следующее в config / initialers / devise.rb

#config.sign_out_via = :delete
4 голосов
/ 17 мая 2013

Правильный способ решения этой проблемы объясняется на вики-странице устройства: 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)

0 голосов
/ 31 мая 2016

Когда мне нужно использовать что-то подобное в test.env :

  visit destroy_user_session_path

это работа для меня, но, возможно, это не правильно)

конфиг / INIT / devise.rb

  # The default HTTP method used to sign out a resource. Default is :delete.
  if Rails.env.test?
    config.sign_out_via = :get
  else
    config.sign_out_via = :delete
  end
0 голосов
/ 24 июля 2011

У меня точно такая же проблема, но с приложением Rails / Sinatra. У меня есть Devise для Rails, и выход из системы работает. У меня есть приложение GuestApp Sinatra, работающее в lib, которое прекрасно работает, за исключением ссылки на выход из системы. Я пытаюсь принудительно установить data-method = "delete" в ссылке выхода из системы 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>
...