Skip to main content

Using an anonymous controller

Use the controller method to define an anonymous controller that will inherit from the described class. This is useful for specifying behavior like global error handling.

To specify a different base class you can pass the class explicitly to the controller method:

controller(BaseController)

You can also disable base type inference, in which case anonymous controllers will inherit from ApplicationController instead of the described class by default:

RSpec.configure do |c|
c.infer_base_class_for_anonymous_controllers = false
end

RSpec.describe BaseController, type: :controller do
controller do
def index; end

# this normally creates an anonymous `BaseController` subclass,
# however since `infer_base_class_for_anonymous_controllers` is
# disabled, it creates a subclass of `ApplicationController`
end
end

Specify error handling in ApplicationController with redirect

Given a file named "spec/controllers/application_controller_spec.rb" with:

require "rails_helper"

class ApplicationController < ActionController::Base
class AccessDenied < StandardError; end

rescue_from AccessDenied, :with => :access_denied

private

def access_denied
redirect_to "/401.html"
end
end

RSpec.describe ApplicationController, type: :controller do
controller do
def index
raise ApplicationController::AccessDenied
end
end

describe "handling AccessDenied exceptions" do
it "redirects to the /401.html page" do
get :index
expect(response).to redirect_to("/401.html")
end
end
end

When I run rspec spec

Then the examples should all pass.

Specify error handling in ApplicationController with render

Given a file named "spec/controllers/application_controller_spec.rb" with:

require "rails_helper"

class ApplicationController < ActionController::Base
class AccessDenied < StandardError; end

rescue_from AccessDenied, :with => :access_denied

private

def access_denied
render "errors/401"
end
end

RSpec.describe ApplicationController, type: :controller do
controller do
def index
raise ApplicationController::AccessDenied
end
end

describe "handling AccessDenied exceptions" do
it "renders the errors/401 template" do
get :index
expect(response).to render_template("errors/401")
end
end
end

When I run rspec spec

Then the examples should all pass.

Specify error handling in a subclass

Given a file named "spec/controllers/application_controller_subclass_spec.rb" with:

require "rails_helper"

class ApplicationController < ActionController::Base
class AccessDenied < StandardError; end
end

class FoosController < ApplicationController

rescue_from ApplicationController::AccessDenied,
:with => :access_denied

private

def access_denied
redirect_to "/401.html"
end
end

RSpec.describe FoosController, type: :controller do
controller(FoosController) do
def index
raise ApplicationController::AccessDenied
end
end

describe "handling AccessDenied exceptions" do
it "redirects to the /401.html page" do
get :index
expect(response).to redirect_to("/401.html")
end
end
end

When I run rspec spec

Then the examples should all pass.

Infer base class from the described class

Given a file named "spec/controllers/base_class_can_be_inferred_spec.rb" with:

require "rails_helper"

class ApplicationController < ActionController::Base; end

class FoosController < ApplicationController; end

RSpec.describe FoosController, type: :controller do
controller do
def index
render :plain => "Hello World"
end
end

it "creates anonymous controller derived from FoosController" do
expect(controller).to be_a_kind_of(FoosController)
end
end

When I run rspec spec

Then the examples should all pass.

Use name and controller_name from the described class

Given a file named "spec/controllers/get_name_and_controller_name_from_described_class_spec.rb" with:

require "rails_helper"

class ApplicationController < ActionController::Base; end
class FoosController < ApplicationController; end

RSpec.describe "Access controller names", type: :controller do
controller FoosController do
def index
@name = self.class.name
@controller_name = controller_name
render :plain => "Hello World"
end
end

before do
get :index
end

it "gets the class name as described" do
expect(assigns[:name]).to eq('FoosController')
end

it "gets the controller_name as described" do
expect(assigns[:controller_name]).to eq('foos')
end
end

When I run rspec spec

Then the examples should all pass.

Invoke around_filter and around_action in base class

Given a file named "spec/controllers/application_controller_around_filter_spec.rb" with:

require "rails_helper"

class ApplicationController < ActionController::Base
around_action :an_around_filter

def an_around_filter
@callback_invoked = true
yield
end
end

RSpec.describe ApplicationController, type: :controller do
controller do
def index
render :plain => ""
end
end

it "invokes the callback" do
get :index

expect(assigns[:callback_invoked]).to be_truthy
end
end

When I run rspec spec

Then the examples should all pass.

Anonymous controllers only create resource routes

Given a file named "spec/controllers/application_controller_spec.rb" with:

require "rails_helper"

if defined?(ActionController::UrlGenerationError)
ExpectedRoutingError = ActionController::UrlGenerationError
else
ExpectedRoutingError = ActionController::RoutingError
end

RSpec.describe ApplicationController, type: :controller do
controller do
def index
render :plain => "index called"
end

def create
render :plain => "create called"
end

def new
render :plain => "new called"
end

def show
render :plain => "show called"
end

def edit
render :plain => "edit called"
end

def update
render :plain => "update called"
end

def destroy
render :plain => "destroy called"
end

def willerror
render :plain => "will not render"
end
end

describe "#index" do
it "responds to GET" do
get :index
expect(response.body).to eq "index called"
end

it "also responds to POST" do
post :index
expect(response.body).to eq "index called"
end

it "also responds to PUT" do
put :index
expect(response.body).to eq "index called"
end

it "also responds to DELETE" do
delete :index
expect(response.body).to eq "index called"
end
end

describe "#create" do
it "responds to POST" do
post :create
expect(response.body).to eq "create called"
end

# And the rest...
%w{get post put delete}.each do |calltype|
it "responds to #{calltype}" do
send(calltype, :create)
expect(response.body).to eq "create called"
end
end
end

describe "#new" do
it "responds to GET" do
get :new
expect(response.body).to eq "new called"
end

# And the rest...
%w{get post put delete}.each do |calltype|
it "responds to #{calltype}" do
send(calltype, :new)
expect(response.body).to eq "new called"
end
end
end

describe "#edit" do
it "responds to GET" do
get :edit, :params => { :id => "anyid" }
expect(response.body).to eq "edit called"
end

it "requires the :id parameter" do
expect { get :edit }.to raise_error(ExpectedRoutingError)
end

# And the rest...
%w{get post put delete}.each do |calltype|
it "responds to #{calltype}" do
send(calltype, :edit, :params => {:id => "anyid"})
expect(response.body).to eq "edit called"
end
end
end

describe "#show" do
it "responds to GET" do
get :show, :params => { :id => "anyid" }
expect(response.body).to eq "show called"
end

it "requires the :id parameter" do
expect { get :show }.to raise_error(ExpectedRoutingError)
end

# And the rest...
%w{get post put delete}.each do |calltype|
it "responds to #{calltype}" do
send(calltype, :show, :params => {:id => "anyid"})
expect(response.body).to eq "show called"
end
end
end

describe "#update" do
it "responds to PUT" do
put :update, :params => { :id => "anyid" }
expect(response.body).to eq "update called"
end

it "requires the :id parameter" do
expect { put :update }.to raise_error(ExpectedRoutingError)
end

# And the rest...
%w{get post put delete}.each do |calltype|
it "responds to #{calltype}" do
send(calltype, :update, :params => {:id => "anyid"})
expect(response.body).to eq "update called"
end
end
end

describe "#destroy" do
it "responds to DELETE" do
delete :destroy, :params => { :id => "anyid" }
expect(response.body).to eq "destroy called"
end

it "requires the :id parameter" do
expect { delete :destroy }.to raise_error(ExpectedRoutingError)
end

# And the rest...
%w{get post put delete}.each do |calltype|
it "responds to #{calltype}" do
send(calltype, :destroy, :params => {:id => "anyid"})
expect(response.body).to eq "destroy called"
end
end
end

describe "#willerror" do
it "cannot be called" do
expect { get :willerror }.to raise_error(ExpectedRoutingError)
end
end
end

When I run rspec spec

Then the examples should all pass.

Draw custom routes for anonymous controllers

Given a file named "spec/controllers/application_controller_spec.rb" with:

require "rails_helper"

RSpec.describe ApplicationController, type: :controller do
controller do
def custom
render :plain => "custom called"
end
end

specify "manually draw the route to request a custom action" do
routes.draw { get "custom" => "anonymous#custom" }

get :custom
expect(response.body).to eq "custom called"
end
end

When I run rspec spec

Then the examples should all pass.

Draw custom routes for anonymous controllers which don't inherit from application controller

Given a file named "spec/controllers/other_controller_spec.rb" with:

require "rails_helper"
class OtherController < ActionController::Base
end

RSpec.describe OtherController, type: :controller do
controller do
def custom
render :plain => "custom called"
end
end

specify "manually draw the route to request a custom action" do
routes.draw { get "custom" => "other#custom" }

get :custom
expect(response.body).to eq "custom called"
end
end

When I run rspec spec

Then the examples should all pass.

Draw custom routes for defined controllers

Given a file named "spec/controllers/application_controller_spec.rb" with:

require "rails_helper"

class FoosController < ApplicationController; end

RSpec.describe ApplicationController, type: :controller do
controller FoosController do
def custom
render :plain => "custom called"
end
end

specify "manually draw the route to request a custom action" do
routes.draw { get "custom" => "foos#custom" }

get :custom
expect(response.body).to eq "custom called"
end
end

When I run rspec spec

Then the examples should all pass.

Works with namespaced controllers

Given a file named "spec/controllers/namespaced_controller_spec.rb" with:

require "rails_helper"

class ApplicationController < ActionController::Base; end

module Outer
module Inner
class FoosController < ApplicationController; end
end
end

RSpec.describe Outer::Inner::FoosController, type: :controller do
controller do
def index
@name = self.class.name
@controller_name = controller_name
render :plain => "Hello World"
end
end

it "creates anonymous controller derived from the namespace" do
expect(controller).to be_a_kind_of(Outer::Inner::FoosController)
end

it "gets the class name as described" do
expect{ get :index }.to change{
assigns[:name]
}.to eq('Outer::Inner::FoosController')
end

it "gets the controller_name as described" do
expect{ get :index }.to change{
assigns[:controller_name]
}.to eq('foos')
end
end

When I run rspec spec

Then the examples should all pass.

Refer to application routes in the controller under test

Given a file named "spec/controllers/application_controller_spec.rb" with:

require "rails_helper"

Rails.application.routes.draw do
match "/login" => "sessions#new", :as => "login", :via => "get"
end

RSpec.describe ApplicationController, type: :controller do
controller do
def index
redirect_to login_url
end
end

it "redirects to the login page" do
get :index
expect(response).to redirect_to("/login")
end
end

When I run rspec spec

Then the examples should all pass.