【问题标题】:ruby on rails 3 tutorial - failing integration test chapter 9 - ActionController::RoutingError: No route matches [GET] "/signout"ruby on rails 3 教程 - 集成测试第 9 章失败 - ActionController::RoutingError: No route matches [GET] "/signout"
【发布时间】:2012-11-08 20:40:29
【问题描述】:

我在 Michael Hartl 的 Ruby on Rails 3 教程(本书的 Kindle 早期版本,而不是在线版本)的第 9 章结束时遇到了失败的集成测试 (users_spec.rb)。我一直在寻找一段时间,但到目前为止我发现的建议都没有对我有用。我下载了第一版和第二版教程的源代码,它们运行良好,所以看起来我的应用程序配置有问题,但作为一个rails新手,感觉就像大海捞针所以欢迎任何帮助。

错误信息:ActionController::RoutingError: No route matches [GET] "/signout"

users_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 %>
    ...

我的 routes.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
    ...

sessions_helper.rb 包含这个

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

这是我执行 rake 路由时得到的结果

    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

我的 gemfile 看起来像这样

    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

当运行 bundler install 时,我得到这个输出:

    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 运行我的应用程序时,它也会失败,因此这不仅仅是 webrat 或集成失败的问题。在 firbug 中查看 html 源代码时,它表明我希望显示的 javascript 不存在

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

当点击“退出”时,这是结果

【问题讨论】:

  • 就您所展示的内容而言,我没有发现任何问题。照常做,清除浏览器缓存,安装包,重启服务器,看看是否可行。
  • 在您的user_spec 文件中,水豚如何知道您要使用此代码单击哪个按钮:click_button?指定按钮的 id、文本或值,然后重试
  • 哇,我才意识到你甚至没有使用水豚
  • 确实,我正在使用 webrat。我很快尝试切换到 capybara,但由于我猜的语法差异,导致 23 次测试失败(包括这篇文章所涉及的测试)。因此,在将所有这些测试调整为 capybara 语法之前,我想确保这个测试是成功的。
  • 如果你还没有意识到,你不应该使用 rails 3.2 来学习 3.0 教程,以避免不必要的挫折。

标签: ruby-on-rails ruby-on-rails-3 rspec rails-routing


【解决方案1】:

执行摘要

您使用的是webrat 而不是capybara,这可以解释为什么我在下面描述的问题出现了。变化:

gem 'webrat'

gem 'capybara'

在您的 Gemfile 中,删除请求的 javascript 点击处理程序应该可以正常工作。

有关所涉及问题的更深入讨论,请参阅下面的原始答案。

原始答案

这是一个棘手的问题。在我看来,问题在于您的测试没有打开 javascript。

一些背景知识:Rails 使用名为 jquery-ujs 的脚本适配器来执行各种操作,例如强制确认对话框(当您尝试删除记录时收到“您确定吗?”消息)并从超链接(因此您可以单击“删除”链接,您的浏览器将发送删除请求)。

在你的 routes.rb 中,你有:

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

这就是说:当 DELETE 请求到达 URL /signout 时,将用户注销。但是,默认情况下,当您单击页面上指向 URL /signout 的链接时,您的浏览器将发送 GET 请求,而不是 DELETE 请求。

当您在此处对link_to 的调用中包含选项method: destroy 时:

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

它的作用是告诉 Rails 将属性 data-method="delete" 添加到视图中链接的属性中(检查实际呈现的 HTML 以确保这是真的)。这个属性就像一个标志:它告诉 jquery-ujs 添加一个单击处理程序,它将对该元素的任何单击从 GET 转换为 DELETE。有关详细信息,请参阅this article。另请注意,Hartl 在a footnote 中提到了这一点。

但是如果在这种情况下没有启用 javascript 会发生什么?好吧,没有添加最后一次点击处理程序,您的浏览器会将指向/signout 的链接视为页面上的任何其他链接,因此当您单击它时,您会向该 URL 发送 GET 请求。然而,Rails 没有任何对/signout 的 GET 请求的路径,因此它会吐出错误:

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 gem 添加到您的 Gemfile 并使用 Capybara.javascript_driver = :webkit 设置驱动程序。有关在 Rspec 请求规范中使用 javascript 的详细信息,请参阅 this Railscast 测试 javascript 部分。

【讨论】:

  • 我刚刚在我的参考副本上验证了这一点,并阅读了github.com/jnicklas/capybara/issues/64,这意味着method: :delete 即使使用rack_test 也应该可以正常工作。有趣的是我有 friendly_forwardings_spec 失败而不是 users_spec (两者都是涉及注销的请求规范)。顺便说一句,旧版本的教程中不存在 via: :delete 约束。
  • 你的意思是你验证了这应该在没有js: true的情况下工作?我不太确定为什么 js 在链接上不起作用,但从错误中很明显我在答案的前半部分写的内容是正确的。虽然 Hartl 的教程没有提到任何关于此的内容,但我也觉得很奇怪,所以我猜这是特定于 OP 环境的配置问题。
  • 是的,这可能是水豚或其他一些依赖项的错误,但我相信测试应该与 rack_test 一起工作
  • 可能相关:我在捆绑器输出中看到 webrat 但没有看到 capybara。
  • 哦,这可能是问题所在,OP 没有使用 capybara!请参阅 Gemfile。这样就可以解释了。
【解决方案2】:

我刚刚注意到您正在使用 webrat:

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

该教程的最后一个版本仍然使用 webrat 是 3.0,而您引用的测试版本来自 here,这意味着您正在尝试遵循较旧的 3.0 教程而不使用 Gemfile已经过测试可以使用它。

虽然在处理data-method: :delete 方面可能与水豚有一个issue,因为行为将在下一个主要版本中改变,但这不是您面临的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-15
    • 1970-01-01
    • 2017-04-10
    • 1970-01-01
    • 2022-12-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多