【问题标题】:RSpec test failing for routes that accept id param接受 id 参数的路由的 RSpec 测试失败
【发布时间】:2015-07-07 17:48:04
【问题描述】:

我正在尝试在我的 Rails 应用程序中测试 ArticlesController。所有不接受参数的路线都在通过。但是所有需要 id 参数的路由都失败了。

失败:

  1) ArticlesController should find article by id
     Failure/Error: get :info, id: @article[:id]
     ActionController::UrlGenerationError:
       No route matches {:action=>"info", :controller=>"articles", :id=>"60"}
     # ./spec/controllers/articles_controller_spec.rb:26:in `block (2 levels) in <top (required)>'

  2) ArticlesController should export folder
     Failure/Error: get :export_folder, id: @article[:id]
     ActionController::UrlGenerationError:
       No route matches {:action=>"export_folder", :controller=>"articles", :id=>"60"}
     # ./spec/controllers/articles_controller_spec.rb:56:in `block (2 levels) in <top (required)>'

  3) ArticlesController should export an article by id
     Failure/Error: get :export, id: @article[:id]
     ActionController::UrlGenerationError:
       No route matches {:action=>"export", :controller=>"articles", :id=>"60"}
     # ./spec/controllers/articles_controller_spec.rb:50:in `block (2 levels) in <top (required)>'

config/routes.rb

get '/articles/list' => 'articles#list', defaults: { format: :html }
  get '/articles/trendlist' => 'articles#trendlist', defaults: { format: :html }
  get '/articles/show/:id' => 'articles#show', defaults: { format: :html }, as: :show_article
  get '/articles/index'
  get '/articles/info/:id' => 'articles#info', as: :article_info
  get '/articles/export/:id' => 'articles#export', as: :export_article
  get '/articles/view/:id' => 'articles#view'
  get '/articles/favorite/:id' => 'articles#favorite'
  get '/articles/trending' => 'articles#trending', defaults: { format: :json }
  get '/articles/deleted' => 'articles#deleted', defaults: { format: :json }
  get '/articles/csv/:id' => 'articles#csv'
  get '/articles/export_folder/:id' => 'articles#export_folder', as: :export_folder

spec/controllers/articles_controller.rb

require 'spec_helper'

describe ArticlesController do
  before(:all) do
    Article.destroy_all
    Comfy::Cms::Layout.destroy_all
    Comfy::Cms::Site.destroy_all
    site = FactoryGirl.create(:cms_site)
    layout = FactoryGirl.create(:cms_layout, site_id: site[:id])
    @article = FactoryGirl.create(:cms_page, layout_id: layout[:id], site_id: site[:id])
  end

  it 'should index articles' do
    get :index
    expect(response.response_code).to eq(200)
    expect(response.headers).to include( 'Content-Type' => 'application/json; charset=utf-8')
  end

  its 'should list articles' do
    get :list
    expect(response.response_code).to eq(200)
    expect(response.headers).to include( 'Content-Type' => 'text/html; charset=utf-8')
  end

  it 'should find article by id' do
    get :info, id: @article[:id]
    expect(response.response_code).to eq(200)
    expect(response.headers).to include( 'Content-Type' => 'application/json; charset=utf-8')
  end

  it 'should list deleted articles' do
    get :deleted
    expect(response.response_code).to eq(200)
    expect(response.headers).to include( 'Content-Type' => 'application/json; charset=utf-8')
  end

  it 'should list trending articles' do
    get :trending
    expect(response.response_code).to eq(200)
    expect(response.headers).to include( 'Content-Type' => 'application/json; charset=utf-8')
  end

  it 'should update trending articles' do
    get :trendlist
    expect(response.response_code).to eq(200)
    expect(response.headers).to include( 'Content-Type' => 'text/html; charset=utf-8')
  end

  it 'should export an article by id' do
    get :export, id: @article[:id]
    expect(response.response_code).to eq(200)
    expect(response.headers).to include( 'Content-Type' => 'text/csv; charset=utf-8')
  end

  it 'should export folder' do
    get :export_folder, id: @article[:id]
    response.response_code.should eq(200)
    expect(response.headers).to include( 'Content-Type' => 'text/html; charset=utf-8')
  end
end

耙路线

Prefix Verb   URI Pattern                                                               Controller#Action
                                        tags GET    /tags(.:format)                                                           tags#index
                               articles_list GET    /articles/list(.:format)                                                  articles#list
                          articles_trendlist GET    /articles/trendlist(.:format)                                             articles#trendlist
                                    articles GET    /articles/show/:id(.:format)                                              articles/articles#show
                              articles_index GET    /articles/index(.:format)                                                 articles#index
                                             GET    /articles/info/:id(.:format)                                              articles/articles#info
                                             GET    /articles/export/:id(.:format)                                            articles/articles#export
                                             GET    /articles/view/:id(.:format)                                              articles/articles#view
                                             GET    /articles/favorite/:id(.:format)                                          articles/articles#favorite
                           articles_trending GET    /articles/trending(.:format)                                              articles#trending
                            articles_deleted GET    /articles/deleted(.:format)                                               articles#deleted
                                             GET    /articles/csv/:id(.:format)                                               articles/articles#csv
                                             GET    /articles/export_folder/:id(.:format)                                     articles/articles#export_folder

app/controllers/articles_controller.rb

class ArticlesController < ApplicationController
  include ArticlesHelper

  before_action :set_default_response_format, except: [:pdf, :show, :list, :trendlist, :export_folder]

  def index
    @articles = SearchArticlesCommand.new(params).execute
  end

  def deleted
    @dlist = Article.deleted.map(&:article_id)
    render :ids, status: :ok
  end

  def info
    id = params[:id].to_i
    @article = Article.published.find_by(id: id)
  end

  def list
    @articles = Article.folder
    render 'articles/list'
  end

  def favorite
    ...
    render json: { result: true, is_liked: "#{is_liked}" }
  end

  def view
    ...
    render json: { result: true }
  end

  def trending
    load_trending_articles
  end

  def trendlist
    load_trending_articles
    render 'articles/trendlist'
  end

  def export
    id = params[:id].to_i
    @article = Article.published.find_by(id: id)

    render pdf: @article.label.gsub(/\s/, '_'),
           template: 'articles/export.pdf.erb',
           dispostion: 'attachment',
           locals: { paragraphs: @article.paragraphs, images: @article.images }

【问题讨论】:

  • 添加您的ArticlesController 的内容。在顶部指定文件路径和名称作为注释。
  • 你的控制器真的在spec/controllers/articles_controller.rb而不是/app/controllers吗?

标签: ruby-on-rails unit-testing rspec


【解决方案1】:

这并不是namespace 的真正用途。你可以阅读更多关于它的信息here。改用resources 并为id 指定member

resources :articles, only: [] do
  collection do 
    get :list
    get :trendlist
    get :trending
    get :deleted
  end

  member do
    get :info
    get :export
    get :view
    get :favorite
    get :csv
    get :export_folder
  end
end

get 'articles/index', to: 'articles#index'
get 'articles/show/:id', to: 'articles#show'

【讨论】:

  • 我无法使用资源,因为索引视图将是 articles/ 而不是 articles/index/,后者已被无法更改的客户端使用。
  • 它实际上是相同的情况,所以它看起来像only: [],感觉不对
  • 你是什么意思'相同的情况'? show 操作的情况是否相同?
  • 节目需要通过articles/show/:id而不是article/:id访问
  • 我会将路由文件更新到我接手这个项目之前的状态。
【解决方案2】:

如果您查看rake routes 的输出,您会发现Rails 正在寻找articles/articles#show 等。namespace 用于创建位于命名空间(duh)中的路由,例如/admin/tools,它将根到Admin::ToolsController.

您可以改用scope,它添加了一个url前缀而不是命名空间或resources

resources :articles, only: [:show, :index] do
  member do
    get 'info'
    get 'export' # Use /articles/1.format instead.
    get 'view' # Do you need this? Code smell!
    get 'favorite' # should be post - GET should never create or alter a resource.
    get 'csv' # remove - use /articles/1.csv instead
    get 'show' # /articles/show/3
  end
  collection do
    get 'trending'
    get 'deleted'
    get 'trendlist'
    get 'list' # Do you need this? Code smell!
    get 'index' # /articles/index
  end
end

我还会质疑为什么您实际上需要超出标准 CRUD 集的这么多路由。 尤其是视图和显示、列表和索引等语义极其丰富的路由。

我会在一组较小的路由周围使用查询参数,因为它可以减少所有级别的重复量。

/articles?filter=deleted => index
/articles?filter=trending

Rails 还具有内置的 CSV mime 类型,因此您可以这样做:

/articles/5.csv

class ProductsController < ApplicationController
  def show
    @article = Article.order(:name)
    respond_to do |format|
      format.html
      format.csv { render text: @article.to_csv }
    end
  end
end

使用@article[:id]@article.id 确实有效,但它的单调且速度稍慢,因为rails 必须通过[] 方法才能找到getter 方法。在这种情况下这不是什么大问题,但在处理大量对象时就不是什么大问题了。

【讨论】:

  • 我已经有客户在生产中更改 api(使用过滤器)目前不是我的选择
  • 好吧,太糟糕了。但如果您将来转向版本化 API,我肯定会考虑它。
  • /articles/show/:id 路由更新了答案。我建议您read the guide - 自定义路线真的很简单。
  • 有很多代码异味,但我们可以更改它,因为必须更新客户端。
  • 是的,我明白了,我对你有感觉 - 从不知道自己在做什么的人那里继承项目很糟糕。我会在上面的资源中添加带有get '/articles/export/:id/' 的奇怪路由,然后尽可能尝试转向更健全的版本化 API。
猜你喜欢
  • 2014-08-02
  • 1970-01-01
  • 1970-01-01
  • 2013-10-14
  • 2012-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多