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.
精彩评论