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.
Since RSpec 2.14 you can use the following:
describe "test controller routing" do
routes { Testy::Engine.routes }
# ...
end
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.
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
This worked for me:
# spec_helper.rb
RSpec.configure do |config|
config.include MyEngine::Engine.routes.url_helpers
end
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.
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