During the migration of a huge application from rails2 to rails3 I encountered a seriously baflling error.

In almost every controller spec, i got the the following error:

Caught ActionController::RoutingError: No route matches {:controller=>"home"}

This got me puzzling. If i ran a single controller-spec it worked, running the whole bunch failed (using rake spec or rspec spec/controllers/**/*_spec.rb). So I posted a question on stackoverflow and on the rspec-mailing-list.

When googling, all related issues seemed to be caused by actual errors in config/routes.rb, but my application was working, it was generating all links correctly. It only failed when testing, and only when running the full suite.

What i did to solve this, was actually compare the log-files of a single group of working controllers with the total set of controllers. And there it was: the routing table was erased by the first spec i ran.

We have a base controller class where the shared behaviour for our API-controllers is placed. To test this controller we create, inside our spec, a new controller-class that derives from that baseclass and add a new route. In rails2 and rspec1 this spec looked as follows (simplified):

require 'spec_helper'

class WsTestController < Ws::BaseController
  def index
  end
end

ActionController::Routing::Routes.draw do |map|
  map.connect ':controller/:action/:id'
  map.connect ':controller/:action/:id.:format'
end

describe WsTestController do
  context "without authentication" do
    it("asks for Basic authentication") {
      get :index
      response.status.should contain("401")
    }
  end
  context "with authentication" do
    # .. snipped the rest ..
  end

end

Now apparently what happens now, in rails3, is that instead of adding routes, the entire routing table is replaced by these routes. Whoops. So in effect, this was almost the only controller-spec that actually worked.

When i disabled this spec, or more specifically the part where the routes are manhandled, all my controller and view specs worked again.

So how do you fix this? How can you dynamically add routes in rails3 at runtime?
Luckily google found the solution here.

So my working spec for rails3/rspec2 looks as follows (and that doesn’t interfere with my other specs) :

require 'spec_helper'

class WsTestController < Ws::BaseController
  def index
    render :text => 'this worked'
  end
end

describe WsTestController do
  before(:each) do
    begin
      _routes = Dpplus::Application.routes
      _routes.disable_clear_and_finalize = true
      _routes.clear!
      Dpplus::Application.routes_reloader.paths.each{ |path| load(path) }
      _routes.draw do
        # here you can add any route you want
        get "ws_test/index"
      end
      ActiveSupport.on_load(:action_controller) { _routes.finalize! }
    ensure
      _routes.disable_clear_and_finalize = false
    end
  end

  context "without authentication" do
    it("asks for Basic authentication") {
      get :index
      response.status.should contain("401")
      response.header["WWW-Authenticate"].should contain('Basic')
    }
  end
  context "with authentication" do
    # ..snipped ...
  end

end

Hope this helps :)