20
votes

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.

6
do you see the route when you run rake routes?Chris Drappier
do you have require 'spec_helper' in index_routing_spec.rb?squarism
rake routes doesn't work in rails 3.1 engines. Perhaps it's a bug but it's explained here: stackoverflow.com/questions/7431687/…Cameron Pope
@squarism, I do. I neglected to include that in the code sampleCameron Pope

6 Answers

12
votes

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

11
votes

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.

11
votes

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
votes

This worked for me:

# spec_helper.rb
RSpec.configure do |config|
  config.include MyEngine::Engine.routes.url_helpers
end
1
votes

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.

1
votes

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.