问题
I am trying to write some routing specs for a mountable rails 3.1 engine. I have working model and controller specs, but I cannot figure out how to specify routes.
For a sample engine, 'testy', every approach I try ends with the same error:
ActionController::RoutingError: No route matches "/testy"
I've tried both Rspec and Test::Unit syntax (spec/routing/index_routing_spec.rb):
describe "test controller routing" do
it "Routs the root to the test controller's index action" do
{ :get => '/testy/' }.should route_to(:controller => 'test', :action => 'index')
end
it "tries the same thing using Test::Unit syntax" do
assert_routing({:method => :get, :path => '/testy/', :use_route => :testy}, {:controller => 'test', :action => 'index'})
end
end
I've laid out the routes correctly (config/routes.rb):
Testy::Engine.routes.draw do
root :to => 'test#index'
end
And mounted them in the dummy app (spec/dummy/config/routes.rb):
Rails.application.routes.draw do
mount Testy::Engine => "/testy"
end
And running rails server
and requesting http://localhost:3000/testy/
works just fine.
Am I missing anything obvious, or is this just not properly baked into the framework yet?
Update: As @andrerobot points out, the rspec folks have fixed this issue in version 2.14, so I've changed my accepted answer accordingly.
回答1:
Since RSpec 2.14 you can use the following:
describe "test controller routing" do
routes { Testy::Engine.routes }
# ...
end
Source: https://github.com/rspec/rspec-rails/pull/668
回答2:
Try adding a before block with the following:
before(:each) { @routes = Testy::Engine.routes }
That worked for me, as the routing specs use that top level instance variable to test their routes.
回答3:
The answer from Steven Anderson got me most of the way there, but the requests need to be made relative to the engine, rather than the app - probably because this technique replaces the app's routes with the engine's routes, so everything is now relative to the engine. It seems a little fragile to me, but I haven't seen another way that works. If someone posts a cleaner way of doing this, I'll be happy to accept that answer instead.
In the 'dummy' app, if the engine is mounted as follows (spec/dummy/config/routes.rb):
Rails.application.routes.draw do
mount Testy::Engine => "/testy"
end
The following spec will correctly test the root route of the engine using both rspec and test::unit syntax (spec/routing/index_route_spec.rb):
require 'spec_helper'
describe "test controller routing" do
before(:each) { @routes = Testy::Engine.routes }
it "Routes the root to the test controller's index action" do
{ :get => '/' }.should route_to(:controller => 'testy/test', :action => 'index')
end
it "tries the same thing using Test::Unit syntax" do
assert_routing({:method => :get, :path => '/'}, {:controller => 'testy/test', :action => 'index'})
end
end
回答4:
This worked for me:
# spec_helper.rb
RSpec.configure do |config|
config.include MyEngine::Engine.routes.url_helpers
end
回答5:
For me, it was a combination of comments by pretty much everybody involved so far.
First, I started with this simple test:
it "routes / to the widgets controller" do
get('/').should route_to("mozoo/widget#index")
end
This resulted in:
Failures:
1) Mozoo::WidgetController GET widget index routes / to the widgets controller
Failure/Error: get('/').should route_to("mozoo/widget#index")
ActionController::RoutingError:
No route matches {:controller=>"mozoo/widget", :action=>"/"}
# ./spec/controllers/mozoo/widget_controller_spec.rb:9:in `block (3 levels) in <module:Mozoo>'
So I switched from get('/')
to { :get => '/' }
and things started working great. Not sure why. According to lib/rspec/rails/matchers/routing_matchers.rb L102-105, there is no difference, but it makes a difference to me. Regardless, thanks @cameron-pope.
Next, I added another pretty simple and very similar test as that above:
it "routes root_path to the widgets controller" do
{ :get => root_path }.should route_to("mozoo/widget#index")
end
And was getting this error:
Failures:
1) Mozoo::WidgetController GET widget index routes root_path to the widgets controller
Failure/Error: { :get => '/mozoo' }.should route_to("mozoo/widget#index")
No route matches "/mozoo"
# ./spec/controllers/mozoo/widget_controller_spec.rb:14:in `block (3 levels) in <module:Mozoo>'
So I added this:
before(:each) { @routes = Mozoo::Engine.routes }
And got a better/different error:
Failures:
1) Mozoo::WidgetController GET widget index routes root_path to the widgets controller
Failure/Error: { :get => root_path }.should route_to("mozoo/widget#index")
The recognized options <{"controller"=>"mozoo/widget", "action"=>"index", "section"=>"mozoo"}> did not match <{"controller"=>"mozoo/widget", "action"=>"index"}>, difference: <{"section"=>"mozoo"}>.
<{"controller"=>"mozoo/widget", "action"=>"index"}> expected but was
<{"controller"=>"mozoo/widget", "action"=>"index", "section"=>"mozoo"}>.
# ./spec/controllers/mozoo/widget_controller_spec.rb:14:in `block (3 levels) in <module:Mozoo>'
From there, I changed my test to include the section (the namespace my engine is under):
{ :get => root_path }.should route_to(:controller => "mozoo/widget", :action => "index", :section => "mozoo")
And viola, it passed. Thanks @steven-anderson.
This next part is odd. After adding another test for a specific widget which used the widget_path url helper for a named route:
it "will successfully serve the widget show page" do
visit widget_path(:foobar)
response.should be_success
end
The test promptly blowd up on me with:
Failures:
1) GET bubble_summary_row widget will have the content section properly scoped
Failure/Error: visit widget_path(:bubble_summary_row)
NoMethodError:
undefined method `widget_path' for #<RSpec::Core::ExampleGroup::Nested_3:0x0000010748f618>
# ./spec/views/mozoo/widgets/show.html.haml_spec.rb:7:in `block (2 levels) in <module:Mozoo>'
So I added the following spec_helper config entry:
RSpec.configure do |config|
config.include Testy::Engine.routes.url_helpers
end
And BAM! It passed. Thanks @sam-soffes. What makes this odd is that later on when creating this comment, I removed that config entry to try and get the error back and I was unable to reproduce the error simply by removing the config entry. Oh well, I'm moving on. Hopefully this long-winded account helps somebody.
回答6:
Based on this answer I chose the following solution:
#spec/spec_helper.rb
RSpec.configure do |config|
# other code
config.before(:each) { @routes = MyEngine::Engine.routes }
end
The additional benefit is, that you don't need to have the before(:each)
block in every controller-spec.
来源:https://stackoverflow.com/questions/7691594/how-to-test-routes-in-a-rails-3-1-mountable-engine