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.