ruby on rails 3教程 - 失败的集成测试第9章 - ActionController :: RoutingError:没有路由匹配[GET]“/ signout”

时间:2012-11-08 20:40:29

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

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

错误消息:ActionController :: RoutingError:没有路由匹配[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'
    ...

我的sessions_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">

signed in state

点击“退出”时就是结果 failing sign out

2 个答案:

答案 0 :(得分:0)

执行摘要

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

gem 'webrat'

gem 'capybara'

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

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

原始回答

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

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

在您的routes.rb中,您有:

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

这就是说:当DELETE请求到达网址/signout时,请将用户注销。但是,默认情况下,当您单击页面上的链接到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。有关详细信息,请参阅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

如果您正在使用默认的javascript驱动程序Selenium,这将实际打开一个浏览器窗口 - 运行js测试而不用窗口将capybara-webkit gem添加到您的Gemfile并使用{{设置驱动程序1}}。有关在Rspec请求规范中使用javascript的详细信息,请参阅有关测试javascript的this Railscast部分。

答案 1 :(得分:0)

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

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

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

虽然可能是一个关于处理data-method: :delete的资深的Gemfile,因为行为会在下一个主要版本中更改,但这不是问题所在面对。