How to test routes in a Rails 3.1 mountable engine

有些话、适合烂在心里 提交于 2019-11-28 09:02:04

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.

wintersolutions

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!