开发者

No route matches rspec's anonymous controller

Based on my understanding of the rspec spec, I would expect the following example to pass.

describe ApplicationController do

  controller do
    def test
    end
  end

  it "calls actions" do
    get :test
  end

end

Instead it fails with:

No route matches {:controller=>"anonymous", :action=>"test"}

I've even tried defining the route for the "anonymous" con开发者_Python百科troller in the routes file but to no avail. Is there something I'm missing here? This should work, shouldn't it?


In order to use custom routes within an anonymous controller spec you need to amend the route set in a before block. RSpec already sets up the RESTful routes using resources :anonymous in a before block, and restores the original routes in an after block. So to get your own routes just call draw on @routes and add what you need.

Here's an example from an ApplicationController spec that tests the rescue_from CanCan::AccessDenied

require 'spec_helper'

describe ApplicationController
  controller do
    def access_denied
      raise CanCan::AccessDenied
    end
  end

  before do
    @routes.draw do
      get '/anonymous/access_denied'
    end
  end

  it 'redirects to the root when access is denied' do
    get :access_denied
    response.should redirect_to root_path
  end

  it 'sets a flash alert when access is denied' do
    get :access_denied
    flash.alert.should =~ /not authorized/i
  end
end

Update

Handling for this has been improved somewhere around RSpec 2.12. If your using > 2.12 then you no longer need to hook into @routes.

Draw custom routes for anonymous controllers


I was having a similar problem. In my case the solution was including an :id parameter in the get request in the test.

I.e.

get :test, :id => 1

Check your routes and see if you are missing a certain parameter (probably :id), then add that to the test.


It seems that rspec gives you a set of RESTful routes to use. Thus if you only use standard action names in your anonymous controller, you won't get this problem. To date I've never had a reason to use anything other than 'index'.

describe ApplicationController do
  describe 'respond_with_foo' do
    controller do
      def index
        respond_with_foo
      end
    end

    it 'should respond with foo' do
      get :index
      response.should be_foo
    end
  end
end


I'm having the same issue, I read the controller method code which is this (defined here):

def controller(base_class = nil, &body)
  base_class ||= RSpec.configuration.infer_base_class_for_anonymous_controllers? ?
                   controller_class :
                   ApplicationController

  metadata[:example_group][:described_class] = Class.new(base_class) do
    def self.name; "AnonymousController"; end
  end
  metadata[:example_group][:described_class].class_eval(&body)

  # ADD ROUTES IN BEFORE BLOCK
  before do
    @orig_routes, @routes = @routes, ActionDispatch::Routing::RouteSet.new
    @routes.draw { resources :anonymous } # <==== HERE ARE THE ROUTES
  end

  after do
    @routes, @orig_routes = @orig_routes, nil
  end
end

Upper case comments in code are mine, essentially you can only use the standard restful routes when using this method. Also you can't use custom routes in your routes.rb file since they are overwritten for the whole example and restored after it.


I had the same issue and found a solution that worked well for me. The key is to map out those routes in your test spec itself:

require 'spec_helper'

describe ApplicationController do
  #Base class should be inferred
  controller do
    def not_found
      raise ActiveRecord::RecordNotFound
    end
  end

  def with_error_routing
    with_routing do |map|
      map.draw do
        get '/not_found' => "anonymous#not_found",     :as => :not_found
      end
      yield
    end
  end

  describe "handling ActiveRecord::RecordNotFound" do
    it "renders the 404 template" do
      with_error_routing do
        get :not_found
        response.should render_template 'error/404'
      end
    end
  end
end


I was able to work around this with the following patch:

module RSpec::Rails
  module ControllerExampleGroup
    module ClassMethods
      def controller(base_class = nil, &body)
        base_class ||= RSpec.configuration.infer_base_class_for_anonymous_controllers? ?
            controller_class :
            ApplicationController

        metadata[:example_group][:described_class] = Class.new(base_class) do
          def self.name; "AnonymousController"; end
        end
        metadata[:example_group][:described_class].class_eval(&body)

        ######## PATCH START ########
        custom_routes = @custom_routes # Copy over routes to local variable so it will be accessible
        before do
          @orig_routes, @routes = @routes, ActionDispatch::Routing::RouteSet.new
          if custom_routes # if this was set then pass that to the draw function
            @routes.draw &custom_routes
          else
            @routes.draw { resources :anonymous } # else do what it used to do before
          end
          ######### PATCH END #########

          routes = @routes
          described_class.send(:define_method, :_routes) { routes }
        end

        after do
          @routes, @orig_routes = @orig_routes, nil
        end
      end

      ######## PATCH START ########
      def custom_routes &block
        @custom_routes = block
      end
      ######### PATCH END #########
  end
end

end

Require that file in your spec_helper, and then in your test simply do:

    describe ApplicationController do
      describe 'respond_with_foo' do
        custom_routes do
          get :bar, controller: :anonymous, action: :bar
        end
        controller do
          def index
            respond_with_foo
          end
        end

        it 'should respond with foo' do
          get :index
          response.should be_foo
        end
      end
    end

In case anyone asks, I tested this code with infer_base_class_for_anonymous_controllers set to false.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜