45

我用命名空间 Posts 编写了一个 Rails 3.1 引擎。因此,我的控制器位于 app/controllers/posts/ 中,我的模型位于 app/models/posts 等中。我可以很好地测试模型。一种型号的规格看起来像...

module Posts
  describe Post do
    describe 'Associations' do
      it ...
      end

...一切正常。

但是,控制器的规格不起作用。Rails 引擎安装在 /posts,但控制器是 Posts::PostController。因此,测试寻找控制器路由为帖子/帖子。

  describe "GET index" do
    it "assigns all posts as @posts" do
      Posts::Post.stub(:all) { [mock_post] }
       get :index
       assigns(:posts).should eq([mock_post])
    end
  end

产生...

  1) Posts::PostsController GET index assigns all posts as @posts
     Failure/Error: get :index
     ActionController::RoutingError:
     No route matches {:controller=>"posts/posts"}
     # ./spec/controllers/posts/posts_controller_spec.rb:16

我在测试应用程序的路由文件中尝试了各种技巧...:命名空间等,但无济于事。

我该如何进行这项工作?似乎不会,因为引擎将控制器放在/posts,但命名空间将控制器放在/posts/posts 以进行测试。

4

7 回答 7

42

我假设您正在使用虚拟 rails 应用程序测试您的引擎,例如由enginex生成的那个。

您的引擎应该安装在虚拟应用程序中:

spec/dummy/config/routes.rb

Dummy::Application.routes.draw do
  mount Posts::Engine => '/posts-prefix'
end

我的第二个假设是您的引擎是孤立的:

lib/posts.rb

module Posts
  class Engine < Rails::Engine
    isolate_namespace Posts
  end
end

我不知道这两个假设是否真的需要,但这就是我自己的引擎的结构。

解决方法很简单,而不是这个

get :show, :id => 1

用这个

get :show, {:id => 1, :use_route => :posts}

:posts符号应该是您的引擎的名称,而不是它的安装路径。

这是有效的,因为 get 方法参数被直接传递给ActionDispatch::Routing::RouteSet::Generator#initialize在这里定义),而后者又用于@named_route从中获取正确的路由Rack::Mount::RouteSet#generate(参见此处此处)。

陷入铁轨内部很有趣,但相当耗时,我不会每天都这样做;-)。

高温高压

于 2011-04-29T13:48:55.380 回答
22

我通过覆盖提供的getpostputdelete方法来解决这个问题,使其始终use_route作为参数传递。

我以 Benoit 的回答为基础。谢谢哥们!

module ControllerHacks
  def get(action, parameters = nil, session = nil, flash = nil)
    process_action(action, parameters, session, flash, "GET")
  end

  # Executes a request simulating POST HTTP method and set/volley the response
  def post(action, parameters = nil, session = nil, flash = nil)
    process_action(action, parameters, session, flash, "POST")
  end

  # Executes a request simulating PUT HTTP method and set/volley the response
  def put(action, parameters = nil, session = nil, flash = nil)
    process_action(action, parameters, session, flash, "PUT")
  end

  # Executes a request simulating DELETE HTTP method and set/volley the response
  def delete(action, parameters = nil, session = nil, flash = nil)
    process_action(action, parameters, session, flash, "DELETE")
  end

  private

  def process_action(action, parameters = nil, session = nil, flash = nil, method = "GET")
    parameters ||= {}
    process(action, parameters.merge!(:use_route => :my_engine), session, flash, method)
  end
end

RSpec.configure do |c|
  c.include ControllerHacks, :type => :controller
end
于 2011-10-05T04:13:38.580 回答
19

使用 rspec-railsroutes指令:

describe MyEngine::WidgetsController do
  routes { MyEngine::Engine.routes }

  # Specs can use the engine's routes & named URL helpers
  # without any other special code.
end

RSpec Rails 2.14 官方文档

于 2013-07-09T00:10:44.650 回答
5

基于这个答案,我选择了以下解决方案:

#spec/spec_helper.rb
RSpec.configure do |config|
 # other code
 config.before(:each) { @routes = UserManager::Engine.routes }
end

额外的好处是,您不需要before(:each)在每个控制器规范中都有块。

于 2013-03-13T19:38:50.633 回答
2

没有或无法使用时的问题解决方案isolate_namespace

module Posts
  class Engine < Rails::Engine
  end
end

在控制器规范中,要修复路由:

get :show, {:id => 1, :use_route => :posts_engine}   

如果你不使用 Rails 将 _engine 添加到你的应用程序路由中isolate_namespace

于 2012-11-20T15:58:08.000 回答
0

我正在为我的公司开发一个为我们正在运行的应用程序提供 API 的 gem。我们仍在使用 Rails 3.0.9,最新的 Rspec-Rails (2.10.1)。我遇到了类似的问题,我在我的 Rails 引擎 gem 中定义了这样的路线。

match '/companyname/api_name' => 'CompanyName/ApiName/ControllerName#apimethod'

我收到一个错误,例如

ActionController::RoutingError:
 No route matches {:controller=>"company_name/api_name/controller_name", :action=>"apimethod"}

事实证明,我只需要在下划线的情况下重新定义我的路线,以便 RSpec 可以匹配它。

match '/companyname/api_name' => 'company_name/api_name/controller_name#apimethod'

我猜 Rspec 控制器测试使用基于下划线大小写的反向查找,而如果您在驼峰或下划线大小写中定义它,Rails 将设置和解释路由。

于 2012-05-25T18:11:54.213 回答
0

已经提到了添加routes { MyEngine::Engine.routes },尽管可以为所有控制器测试指定这一点:

# spec/support/test_helpers/controller_routes.rb
module TestHelpers
  module ControllerRoutes
    extend ActiveSupport::Concern

    included do
      routes { MyEngine::Engine.routes }
    end

  end
end

并用于rails_helper.rb

RSpec.configure do |config|
  config.include TestHelpers::ControllerRoutes, type: :controller
end
于 2020-10-08T15:22:29.120 回答