简体   繁体   中英

How to test routes in a Rails 3.1 mountable engine

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

Source: https://github.com/rspec/rspec-rails/pull/668

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM