如何在rspec中编写Rails 3.1引擎控制器testing?
我写了一个Rails 3.1引擎与命名空间岗位。 因此,我的控制器可以在app / controllers / posts /中find,我在app / models / posts中的模型等。我可以很好的testing模型。 一个模型的规格看起来像…
module Posts describe Post do describe 'Associations' do it ... end
…一切正常。
但是,控制器的规格不起作用。 Rails引擎安装在/ posts,但控制器是Posts :: PostController。 因此,testing将查找控制器路由为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
我已经尝试了各种各样的技巧在testing应用程序的路线文件…:命名空间等,无济于事。
我该如何做这项工作? 它似乎不会,因为引擎把控制器放在/职位,但名称空间将控制器放在/职位/职位为testing目的。
我假设你正在用虚拟rails应用testing你的引擎,就像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
符号应该是你的引擎的名字,而不是它所在的path。
这是ActionDispatch::Routing::RouteSet::Generator#initialize
因为get方法参数直接传递给ActionDispatch::Routing::RouteSet::Generator#initialize
(定义在这里 ),这反过来使用@named_route
从Rack::Mount::RouteSet#generate
获得正确的路由Rack::Mount::RouteSet#generate
(看到这里和这里 )。
陷入铁路内部是有趣的,但相当耗时,我不会每天都这样做;-)。
HTH
我通过覆盖所提供的get
, post
, put
和delete
方法来解决这个问题,使得它总是将use_route
作为parameter passing。
我以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
使用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官方文档
基于这个答案,我select了以下解决scheme:
#spec/spec_helper.rb RSpec.configure do |config| # other code config.before(:each) { @routes = UserManager::Engine.routes } end
额外的好处是,你不需要在每个控制器规范中都有before(:each)
块。
当您没有或无法使用isolate_namespace
时,解决问题的isolate_namespace
:
module Posts class Engine < Rails::Engine end end
在控制器规格中,修复路线:
get :show, {:id => 1, :use_route => :posts_engine}
如果不使用isolate_namespace
Rails会将_engine添加到您的应用程序路由中。
我正在为我公司开发一个gem,为我们正在运行的应用程序提供一个API。 我们仍然使用Rails 3.0.9,最新的Rspec-Rails(2.10.1)。 我遇到了类似的问题,我在Rails引擎中定义了类似的路线。
match '/companyname/api_name' => 'CompanyName/ApiName/ControllerName#apimethod'
我遇到了类似的错误
ActionController::RoutingError: No route matches {:controller=>"company_name/api_name/controller_name", :action=>"apimethod"}
事实certificate,我只是需要重新定义我的路线下划线的情况下,使RSpec可以匹配它。
match '/companyname/api_name' => 'company_name/api_name/controller_name#apimethod'
我猜Rspec控制器testing使用反向查找基于下划线的情况下,而Rails将设置和解释的路线,如果你在camelcase或下划线的情况下定义它。