如何在 rspec 中编写 Rails 3.1 引擎控制器测试?

发布于 2024-10-20 13:06:20 字数 989 浏览 2 评论 0原文

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

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

并且一切正常。

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

  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 处以进行测试。

I have written a Rails 3.1 engine with the namespace Posts. Hence, my controllers are found in app/controllers/posts/, my models in app/models/posts, etc. I can test the models just fine. The spec for one model looks like...

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

... and everything works fine.

However, the specs for the controllers do not work. The Rails engine is mounted at /posts, yet the controller is Posts::PostController. Thus, the tests look for the controller route to be posts/posts.

  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

which yields...

  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

I've tried all sorts of tricks in the test app's routes file... :namespace, etc, to no avail.

How do I make this work? It seems like it won't, since the engine puts the controller at /posts, yet the namespacing puts the controller at /posts/posts for the purpose of testing.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

恍梦境° 2024-10-27 13:06:20

我假设您正在使用虚拟 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(请参阅此处此处)。

深入了解 Rails 内部结构很有趣,但相当耗时,我不会每天都这样做;-)。

华泰

I'm assuming you're testing your engine with a dummy rails app, like the one that would be generated by enginex.

Your engine should be mounted in the dummy app:

In spec/dummy/config/routes.rb:

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

My second assumption is that your engine is isolated:

In lib/posts.rb:

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

I don't know if these two assumptions are really required, but that is how my own engine is structured.

The workaround is quite simple, instead of this

get :show, :id => 1

use this

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

The :posts symbol should be the name of your engine and NOT the path where it is mounted.

This works because the get method parameters are passed straight to ActionDispatch::Routing::RouteSet::Generator#initialize (defined here), which in turn uses @named_route to get the correct route from Rack::Mount::RouteSet#generate (see here and here).

Plunging into the rails internals is fun, but quite time consuming, I would not do this every day ;-) .

HTH

丢了幸福的猪 2024-10-27 13:06:20

我通过重写提供的 getpostputdelete 方法解决了这个问题,使得因此他们总是将 use_route 作为参数传递。

我使用伯努瓦的回答作为基础。谢谢哥们!

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

I worked around this issue by overriding the get, post, put, and delete methods that are provided, making it so they always pass use_route as a parameter.

I used Benoit's answer as a basis for this. Thanks buddy!

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
你的心境我的脸 2024-10-27 13:06:20

使用 rspec-rails routes 指令:

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 官方文档

Use the rspec-rails routes directive:

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 official docs.

昇り龍 2024-10-27 13:06:20

基于 回答 我选择了以下解决方案:

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

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

Based on this answer I chose the following solution:

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

The additional benefit is, that you don't need to have the before(:each) block in every controller-spec.

染年凉城似染瑾 2024-10-27 13:06:20

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

module Posts
  class Engine < Rails::Engine
  end
end

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

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

如果您不使用 isolate_namespace,Rails 会将 _engine 添加到您的应用路由中>。

Solution for a problem when you don't have or cannot use isolate_namespace:

module Posts
  class Engine < Rails::Engine
  end
end

In controller specs, to fix routes:

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

Rails adds _engine to your app routes if you don't use isolate_namespace.

梦里梦着梦中梦 2024-10-27 13:06:20

我正在为我的公司开发一个 gem,它为我们正在运行的应用程序提供 API。我们仍然使用 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 将设置并解释路由。

I'm developing a gem for my company that provides an API for the applications we're running. We're using Rails 3.0.9 still, with latest Rspec-Rails (2.10.1). I was having a similar issue where I had defined routes like so in my Rails engine gem.

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

I was getting an error like

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

It turns out I just needed to redefine my route in underscore case so that RSpec could match it.

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

I guess Rspec controller tests use a reverse lookup based on underscore case, whereas Rails will setup and interpret the route if you define it in camelcase or underscore case.

等待我真够勒 2024-10-27 13:06:20

已经提到了添加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

It was already mentioned about adding routes { MyEngine::Engine.routes }, although it's possible to specify this for all controller tests:

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

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

  end
end

and use in rails_helper.rb:

RSpec.configure do |config|
  config.include TestHelpers::ControllerRoutes, type: :controller
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文