Учебное пособие по ruby ​​on rails 3 - сбой интеграционного теста, глава 9 - ActionController::RoutingError: маршрут не соответствует [GET] /signout

Я нахожусь в конце главы 9 учебника по Ruby on Rails 3 Майкла Хартла (раннее издание книги для Kindle, а не онлайн-версия) и столкнулся с неудачным интеграционным тестом (users_spec.rb). Я искал некоторое время, но ни одно из предложений, которые я нашел до сих пор, не сработало для меня. Я загрузил исходный код учебника как для первого, так и для второго издания, и они работают нормально, так что действительно похоже, что что-то не так в конфигурации моего приложения, но как новичок в рельсах, это похоже на поиск иголки в стоге сена поэтому любая помощь приветствуется.

сообщение об ошибке: ActionController::RoutingError: маршрут не соответствует [GET] "/signout"

user_spec.rb содержит это

    ...
       describe "success" do
          it "should sign a user in and out" do
             user = FactoryGirl.create(:user)
             visit signin_path
             fill_in :email, :with => user.email
             fill_in :password, :with => user.password
             click_button
             controller.should be_signed_in
             click_link "Sign out"
             controller.should_not be_signed_in
          end
       end
    ...

мой application.html.erb выглядит так

    <!DOCTYPE html>
    <html>
    <head>
      <title><%= @title %></title>
      <%= javascript_include_tag 'default' %>
      <%= csrf_meta_tags %>
      <%= render 'layouts/stylesheets' %>
    </head>
    <body>
      <div class='container'>
        <%= render 'layouts/header' %>
        <section class = "round">
          <% flash.each do |key, value| %>
            <div class="flash <%= key %>"><%= value %></div>
          <% end %>
          <%= yield %>
        </section>
        <%= render 'layouts/footer' %>
        <%= debug(params) if Rails.env.development? %>
      </div>
    </body>
    </html>

мой application_controller.rb выглядит так

    class ApplicationController < ActionController::Base
      protect_from_forgery
      include SessionsHelper
    end

мой application.js выглядит так

    // This is a manifest file that'll be compiled into application.js, which will include all the files
    // listed below.
    //
    // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
    // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
    //
    // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
    // the compiled file.
    //
    // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
    // GO AFTER THE REQUIRES BELOW.
    //
    //= require jquery
    //= require jquery_ujs
    //= require_tree .

файл _header.html.erb содержит это

    ...
      <% if signed_in? %>
      <li><%= link_to "Sign out", signout_path, :method => :delete %></li>
      <% else %>
    ...

мой route.rb содержит это

    ...
      get "sessions/new"

      resources :users
      resources :sessions, :only =>[:new, :create, :destroy]

      match '/contact', :to => 'pages#contact'
      match '/about', :to => 'pages#about'
      match '/help', :to => 'pages#help'
      match '/signup', to: 'users#new'

      match '/signin', to: 'sessions#new'
      match '/signout', to: 'sessions#destroy', via: :delete

      root :to => 'pages#home'
    ...

мой session_controller.rb содержит это

    ...
      def destroy
        sign_out
        redirect_to root_path
      end
    ...

session_helper.rb содержит это

    ...
      def sign_out
        cookies.delete(:remember_token)
        @current_user = nil
      end
    ...

это то, что я получаю, когда выполняю рейк-маршруты

    sessions_new GET    /sessions/new(.:format)   sessions#new
           users GET    /users(.:format)          users#index
                 POST   /users(.:format)          users#create
        new_user GET    /users/new(.:format)      users#new
       edit_user GET    /users/:id/edit(.:format) users#edit
            user GET    /users/:id(.:format)      users#show
                 PUT    /users/:id(.:format)      users#update
                 DELETE /users/:id(.:format)      users#destroy
        sessions POST   /sessions(.:format)       sessions#create
     new_session GET    /sessions/new(.:format)   sessions#new
         session DELETE /sessions/:id(.:format)   sessions#destroy
         contact        /contact(.:format)        pages#contact
           about        /about(.:format)          pages#about
            help        /help(.:format)           pages#help
          signup        /signup(.:format)         users#new
          signin        /signin(.:format)         sessions#new
         signout DELETE /signout(.:format)        sessions#destroy

мой гемфайл выглядит так

    source 'https://rubygems.org'

    gem 'rails', '3.2.8'

    gem 'pg'
    gem 'gravatar_image_tag'
    gem 'jquery-rails'

    group :development do
      gem 'rspec-rails'
      gem 'annotate'
    end

    group :test do
      gem 'rspec'
      gem 'webrat'
      gem 'spork'
      gem 'factory_girl_rails'
    end

при запуске пакетной установки я получаю это как вывод:

    Using rake (0.9.2.2)
    Using i18n (0.6.1)
    Using multi_json (1.3.6)
    Using activesupport (3.2.8)
    Using builder (3.0.4)
    Using activemodel (3.2.8)
    Using erubis (2.7.0)
    Using journey (1.0.4)
    Using rack (1.4.1)
    Using rack-cache (1.2)
    Using rack-test (0.6.2)
    Using hike (1.2.1)
    Using tilt (1.3.3)
    Using sprockets (2.1.3)
    Using actionpack (3.2.8)
    Using mime-types (1.19)
    Using polyglot (0.3.3)
    Using treetop (1.4.11)
    Using mail (2.4.4)
    Using actionmailer (3.2.8)
    Using arel (3.0.2)
    Using tzinfo (0.3.33)
    Using activerecord (3.2.8)
    Using activeresource (3.2.8)
    Using annotate (2.5.0)
    Using diff-lcs (1.1.3)
    Using factory_girl (4.1.0)
    Using rack-ssl (1.3.2)
    Using json (1.7.5)
    Using rdoc (3.12)
    Using thor (0.16.0)
    Using railties (3.2.8)
    Using factory_girl_rails (4.1.0)
    Using gravatar_image_tag (1.1.3)
    Using jquery-rails (2.1.3)
    Using nokogiri (1.5.5)
    Using pg (0.14.1)
    Using bundler (1.2.0)
    Using rails (3.2.8)
    Using rspec-core (2.11.1)
    Using rspec-expectations (2.11.3)
    Using rspec-mocks (2.11.3)
    Using rspec (2.11.0)
    Using rspec-rails (2.11.4)
    Using spork (0.9.2)
    Using webrat (0.7.3)

возможно, интересно упомянуть, что при запуске моего приложения через rails оно также дает сбой, так что это не только проблема с веб-сайтом или неудачной интеграцией. при просмотре исходного кода html в firbug он показывает, что javascript, который я ожидал увидеть, отсутствует.

    <script type="text/javascript" src="/assets/jquery.js?body=1">
    <script type="text/javascript" src="/assets/jquery_ujs.js?body=1">

состояние входа

при нажатии «Выйти» это результат failing log out


person lieven baeyens    schedule 08.11.2012    source источник
comment
Я не вижу ничего плохого в том, что вы показали. Сделайте обычное, очистите кеш браузера, установите пакет, перезапустите сервер и посмотрите, работает ли это.   -  person Jason Kim    schedule 09.11.2012
comment
в вашем файле user_spec, как водосвинка узнает, какую кнопку вы хотите нажать с помощью этого кода: click_button? укажите идентификатор, текст или значение кнопки и повторите попытку   -  person Thanh    schedule 09.11.2012
comment
вау, я только что понял, что ты даже не используешь капибару   -  person prusswan    schedule 09.11.2012
comment
действительно, я использую webrat. Я быстро попытался переключиться на капибару, но это привело к 23 неудачным тестам (включая тот, о котором идет речь в этом посте) из-за различий в синтаксисе. Поэтому, прежде чем адаптировать все эти тесты к синтаксису водосвинки, я хочу убедиться, что этот тест прошел успешно.   -  person lieven baeyens    schedule 10.11.2012
comment
Если вы еще не поняли, вам не следует использовать rails 3.2 для следования руководству по 3.0, чтобы избежать ненужных разочарований.   -  person prusswan    schedule 14.11.2012


Ответы (2)


Резюме

Вы используете webrat вместо capybara, что объясняет, почему возникла описанная ниже проблема. Изменять:

gem 'webrat'

to

gem 'capybara'

в вашем Gemfile, и обработчик кликов javascript для запросов DELETE должен работать нормально.

Более подробное обсуждение затронутых вопросов см. в моем первоначальном ответе ниже.

ИСХОДНЫЙ ОТВЕТ

Это сложно. Мне кажется, проблема в том, что javascript не включен для вашего теста.

Немного предыстории: Rails использует скриптовый адаптер под названием jquery-ujs для выполнения различных действий, таких как принудительное диалоговое окно подтверждения. (сообщение «Вы уверены?», которое вы получаете при попытке удалить запись) и делать не-GET-запросы из гиперссылок (так что вы можете щелкнуть ссылку «Удалить», и ваш браузер отправит запрос DELETE).

В вашем route.rb у вас есть:

signout DELETE /signout(.:format)        sessions#destroy

Это говорит о следующем: когда на URL-адрес /signout приходит запрос DELETE, выйдите из системы. Однако по умолчанию, когда вы щелкаете ссылку на странице с URL-адресом /signout, ваш браузер отправляет запрос GET, а не запрос DELETE.

Когда вы включаете опцию method: destroy в свой вызов link_to здесь:

link_to "Sign out", signout_path, :method => :delete

Это говорит Rails добавить атрибут data-method="delete" к атрибутам ссылки в представлении (проверьте фактически отображаемый HTML, чтобы убедиться, что это правда). Этот атрибут похож на флаг: он говорит jquery-ujs добавить обработчик кликов, который превращает любой щелчок по этому элементу из GET в DELETE. Дополнительные сведения см. в этой статье. Также обратите внимание, что Хартл упоминает об этом в сноске.

Но что произойдет, если в этом случае javascript не включен? Что ж, этот обработчик последнего клика не добавляется, и ваш браузер обрабатывает ссылку на /signout как любую другую ссылку на странице, поэтому, когда вы щелкаете по ней, вы отправляете запрос GET на этот URL-адрес. Однако у Rails нет пути для GET-запроса к /signout, поэтому он выдает ошибку:

ActionController::RoutingError: No route matches [GET] “/signout”

Вот в чем причина проблемы.

Теперь, что касается решения, я считаю, что вы должны иметь возможность добавить в свой тест js: true параметр, который включит javascript, например:

it "should sign a user in and out", js: true do
  ...
end

Если вы используете Selenium, драйвер javascript по умолчанию, это фактически откроет окно браузера — чтобы запустить тест js без окна, добавьте capybara-webkit в свой Gemfile и установите драйвер с помощью Capybara.javascript_driver = :webkit. Подробнее см. в разделе этого Railscast о тестировании javascript. при использовании javascript в спецификациях запросов Rspec.

person Chris Salzberg    schedule 08.11.2012
comment
Я только что проверил это на своей эталонной копии и прочитал о github.com/jnicklas/capybara/issues/64., что означает, что method: :delete должно нормально работать даже с rack_test. Что интересно, у меня был сбой friendly_forwardings_spec вместо users_spec (оба являются спецификациями запроса, включающими выход). Кроме того, ограничение via: :delete отсутствует в старых версиях учебника. - person prusswan; 09.11.2012
comment
Вы имеете в виду, что вы проверили, что это должно работать без js: true? Я не совсем понимаю, почему js не работает по ссылке, но из ошибки совершенно ясно, что то, что я написал в первой половине ответа, верно. Мне также показалось странным, что в учебнике Хартла ничего об этом не упоминается, поэтому я предполагаю, что это проблема конфигурации, характерная для среды OP. - person Chris Salzberg; 09.11.2012
comment
да, это может быть ошибка капибары или какая-то другая зависимость, но я считаю, что тест должен работать с рэком_тестом - person prusswan; 09.11.2012
comment
Возможно, связано: я вижу webrat, но не капибару в выходных данных упаковщика. - person Chris Salzberg; 09.11.2012
comment
О, наверное, проблема в том, что ОП не использует капибару! См. Gemfile. Это бы объяснило. - person Chris Salzberg; 09.11.2012
comment
спасибо за контекстную информацию, это действительно помогает лучше понять, как все работает под капотом. Тем временем я пытаюсь поменять местами паутину и капибару. - person lieven baeyens; 10.11.2012
comment
когда я запускаю свое приложение через rails s, при нажатии «Выйти» я получаю ту же ошибку, что и при неудачном тесте интеграции: маршрут не соответствует [GET] /signout. при просмотре исходного кода html в firebug не отображается сценарий javascript, кроме ‹script type=text/javascript src=/assets/default.js›. поэтому кажется, что требуемый ‹script type=text/javascript src=/assets/jquery.js?body=1› ‹script type=text/javascript src=/assets/jquery_ujs.js?body=1› появляется, когда запуск кода учебника, здесь отсутствует. Любая подсказка о том, как решить эту проблему? - person lieven baeyens; 10.11.2012
comment
Обычно в приложении rails у вас есть файл app/assets/javascripts/application.js, и в этом файле у вас есть require директивы для включения различных зависимостей javascript. Чтобы получить jquery и jquery_ujs, вам нужно убедиться, что у вас есть строки //= require jquery и //=require jquery_ujs в таком порядке. Если вы используете default.js вместо application.js, добавьте эти две строки в начало этого файла. - person Chris Salzberg; 10.11.2012

Я только что заметил, что вы используете webrat:

    group :test do
      gem 'rspec'
      gem 'webrat'
      gem 'spork'
      gem 'factory_girl_rails'
    end

Последняя версия этого учебника, в которой все еще используется webrat, — 3.0, а версия теста, на которую вы ссылаетесь, взята из здесь, что означает, что вы пытаетесь следовать старому руководству 3.0 без использования Gemfile, который был протестирован для работы с ним.

Хотя может быть проблема с капибарой в отношении обработки data-method: :delete, так как поведение будет изменено в следующей основной версии, это не проблема, с которой вы столкнулись.

person prusswan    schedule 09.11.2012