我编写了一个带有命名空间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
我在测试应用程序的路径文件...:命名空间等中尝试了各种技巧,但无济于事。
如何使这项工作?似乎它不会,因为引擎将控制器置于/ post,但命名空间将控制器置于/ posts / posts以进行测试。
答案 0 :(得分:42)
我假设您正在使用虚拟导轨应用测试您的引擎,就像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
(已定义here),后者又使用@named_route
从Rack::Mount::RouteSet#generate
获取正确的路由(请参阅here和here)。
插入导轨内部很有趣,但非常耗时,我不会每天都这样做;-)。
HTH
答案 1 :(得分:22)
我通过覆盖提供的get
,post
,put
和delete
方法解决了这个问题,使它们始终通过{{1} }作为参数。
我用Benoit的答案作为基础。谢谢伙计!
use_route
答案 2 :(得分:18)
使用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
答案 3 :(得分:5)
基于this回答,我选择了以下解决方案:
#spec/spec_helper.rb
RSpec.configure do |config|
# other code
config.before(:each) { @routes = UserManager::Engine.routes }
end
附加好处是,您不需要在每个控制器规范中都有before(:each)
块。
答案 4 :(得分:2)
当您没有或不能使用isolate_namespace
时解决问题:
module Posts
class Engine < Rails::Engine
end
end
在控制器规格中,修复路线:
get :show, {:id => 1, :use_route => :posts_engine}
如果您不使用isolate_namespace
,则Rails会将_engine添加到您的应用路由。
答案 5 :(得分:0)
我正在为我的公司开发一个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控制器测试使用基于下划线情况的反向查找,而如果您在camelcase或下划线情况下定义它,Rails将设置和解释该路由。
答案 6 :(得分: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