Module: Shoulda::Matchers::ActionController
Overview
This module provides matchers that are used to test behavior within controllers.
Instance Method Summary collapse
- 
  
    
      #filter_param(key)  ⇒ FilterParamMatcher 
    
    
  
  
  
  
  
  
  
  
  
    
The
filter_parammatcher is used to test parameter filtering configuration. - 
  
    
      #permit(*params)  ⇒ PermitMatcher 
    
    
  
  
  
  
  
  
  
  
  
    
The
permitmatcher tests that an action in your controller receives a allowlist of parameters using Rails' Strong Parameters feature (specifically thatpermitwas called with the correct arguments). - 
  
    
      #redirect_to(url_or_description, &block)  ⇒ RedirectToMatcher 
    
    
  
  
  
  
  
  
  
  
  
    
The
redirect_tomatcher tests that an action redirects to a certain location. - 
  
    
      #render_template(options = {}, message = nil)  ⇒ RenderTemplateMatcher 
    
    
  
  
  
  
  
  
  
  
  
    
The
render_templatematcher tests that an action renders a template or partial. - 
  
    
      #render_with_layout(expected_layout = nil)  ⇒ RenderWithLayoutMatcher 
    
    
  
  
  
  
  
  
  
  
  
    
The
render_with_layoutmatcher asserts that an action is rendered with a particular layout. - 
  
    
      #rescue_from(exception)  ⇒ RescueFromMatcher 
    
    
  
  
  
  
  
  
  
  
  
    
The
rescue_frommatcher tests usage of therescue_frommacro. - 
  
    
      #respond_with(status)  ⇒ RespondWithMatcher 
    
    
  
  
  
  
  
  
  
  
  
    
The
respond_withmatcher tests that an action responds with a certain status code. - 
  
    
      #route(method, path, port: nil)  ⇒ RouteMatcher 
    
    
  
  
  
  
  
  
  
  
  
    
The
routematcher tests that a route resolves to a controller, action, and params; and that the controller, action, and params generates the same route. - 
  
    
      #set_flash  ⇒ SetFlashMatcher 
    
    
  
  
  
  
  
  
  
  
  
    
The
set_flashmatcher is used to make assertions about theflashhash. - 
  
    
      #set_session  ⇒ SetSessionMatcher 
    
    
  
  
  
  
  
  
  
  
  
    
The
set_sessionmatcher is used to make assertions about thesessionhash. - 
  
    
      #use_after_action(callback)  ⇒ CallbackMatcher 
    
    
  
  
  
  
  
  
  
  
  
    
The
use_after_actionmatcher is used to test that an after_action callback is defined within your controller. - 
  
    
      #use_around_action(callback)  ⇒ CallbackMatcher 
    
    
  
  
  
  
  
  
  
  
  
    
The
use_around_actionmatcher is used to test that an around_action callback is defined within your controller. - 
  
    
      #use_before_action(callback)  ⇒ CallbackMatcher 
    
    
  
  
  
  
  
  
  
  
  
    
The
use_before_actionmatcher is used to test that a before_action callback is defined within your controller. 
Instance Method Details
#filter_param(key) ⇒ FilterParamMatcher
The filter_param matcher is used to test parameter filtering
configuration. Specifically, it asserts that the given parameter is
present in config.filter_parameters.
class MyApplication < Rails::Application
  config.filter_parameters << :secret_key
end
# RSpec
RSpec.describe ApplicationController, type: :controller do
  it { should filter_param(:secret_key) }
end
# Minitest (Shoulda)
class ApplicationControllerTest < ActionController::TestCase
  should filter_param(:secret_key)
end
  
      24 25 26  | 
    
      # File 'lib/shoulda/matchers/action_controller/filter_param_matcher.rb', line 24 def filter_param(key) FilterParamMatcher.new(key) end  | 
  
#permit(*params) ⇒ PermitMatcher
The permit matcher tests that an action in your controller receives a
allowlist of parameters using Rails' Strong Parameters feature
(specifically that permit was called with the correct arguments).
Here's an example:
class UsersController < ApplicationController
  def create
    user = User.create(user_params)
    # ...
  end
  private
  def user_params
    params.require(:user).permit(
      :first_name,
      :last_name,
      :email,
      :password
    )
  end
end
# RSpec
RSpec.describe UsersController, type: :controller do
  it do
    params = {
      user: {
        first_name: 'John',
        last_name: 'Doe',
        email: 'johndoe@example.com',
        password: 'password'
      }
    }
    should permit(:first_name, :last_name, :email, :password).
      for(:create, params: params).
      on(:user)
  end
end
# Minitest (Shoulda)
class UsersControllerTest < ActionController::TestCase
  should "(for POST #create) restrict parameters on :user to first_name, last_name, email, and password" do
    params = {
      user: {
        first_name: 'John',
        last_name: 'Doe',
        email: 'johndoe@example.com',
        password: 'password'
      }
    }
    matcher = permit(:first_name, :last_name, :email, :password).
      for(:create, params: params).
      on(:user)
    assert_accepts matcher, subject
  end
end
If your action requires query parameters in order to work, then you'll need to supply them:
class UsersController < ApplicationController
  def update
    user = User.find(params[:id])
    if user.update_attributes(user_params)
      # ...
    else
      # ...
    end
  end
  private
  def user_params
    params.require(:user).permit(
      :first_name,
      :last_name,
      :email,
      :password
    )
  end
end
# RSpec
RSpec.describe UsersController, type: :controller do
  before do
    create(:user, id: 1)
  end
  it do
    params = {
      id: 1,
      user: {
        first_name: 'Jon',
        last_name: 'Doe',
        email: 'jondoe@example.com',
        password: 'password'
      }
    }
    should permit(:first_name, :last_name, :email, :password).
      for(:update, params: params).
      on(:user)
  end
end
# Minitest (Shoulda)
class UsersControllerTest < ActionController::TestCase
  setup do
    create(:user, id: 1)
  end
  should "(for PATCH #update) restrict parameters on :user to :first_name, :last_name, :email, and :password" do
    params = {
      id: 1,
      user: {
        first_name: 'Jon',
        last_name: 'Doe',
        email: 'jondoe@example.com',
        password: 'password'
      }
    }
    matcher = permit(:first_name, :last_name, :email, :password).
      for(:update, params: params).
      on(:user)
    assert_accepts matcher, subject
  end
end
Finally, if you have an action that isn't one of the seven resourceful actions, then you'll need to provide the HTTP verb that it responds to:
Rails.application.routes.draw do
  resources :users do
    member do
      put :toggle
    end
  end
end
class UsersController < ApplicationController
  def toggle
    user = User.find(params[:id])
    if user.update_attributes(user_params)
      # ...
    else
      # ...
    end
  end
  private
  def user_params
    params.require(:user).permit(:activated)
  end
end
# RSpec
RSpec.describe UsersController, type: :controller do
  before do
    create(:user, id: 1)
  end
  it do
    params = { id: 1, user: { activated: true } }
    should permit(:activated).
      for(:toggle, params: params, verb: :put).
      on(:user)
  end
end
# Minitest (Shoulda)
class UsersControllerTest < ActionController::TestCase
  setup do
    create(:user, id: 1)
  end
  should "(for PUT #toggle) restrict parameters on :user to :activated" do
    params = { id: 1, user: { activated: true } }
    matcher = permit(:activated).
      for(:toggle, params: params, verb: :put).
      on(:user)
    assert_accepts matcher, subject
  end
end
  
      203 204 205  | 
    
      # File 'lib/shoulda/matchers/action_controller/permit_matcher.rb', line 203 def permit(*params) PermitMatcher.new(params).in_context(self) end  | 
  
#redirect_to(url_or_description, &block) ⇒ RedirectToMatcher
The redirect_to matcher tests that an action redirects to a certain
location. In a test suite using RSpec, it is very similar to
rspec-rails's redirect_to matcher. In a test suite using Minitest +
Shoulda, it provides a more expressive syntax over
assert_redirected_to.
class PostsController < ApplicationController
  def show
    redirect_to :index
  end
end
# RSpec
RSpec.describe PostsController, type: :controller do
  describe 'GET #show' do
    before { get :show }
    it { should redirect_to(posts_path) }
    it { should redirect_to(action: :index) }
  end
end
# Minitest (Shoulda)
class PostsControllerTest < ActionController::TestCase
  context 'GET #show' do
    setup { get :show }
    should redirect_to('/posts') { posts_path }
    should redirect_to(action: :index)
  end
end
  
      38 39 40  | 
    
      # File 'lib/shoulda/matchers/action_controller/redirect_to_matcher.rb', line 38 def redirect_to(url_or_description, &block) RedirectToMatcher.new(url_or_description, self, &block) end  | 
  
#render_template(options = {}, message = nil) ⇒ RenderTemplateMatcher
The render_template matcher tests that an action renders a template
or partial. In RSpec, it is very similar to rspec-rails's
render_template matcher. In a test suite using Minitest + Shoulda, it
provides a more expressive syntax over assert_template.
class PostsController < ApplicationController
  def show
  end
end
# app/views/posts/show.html.erb
<%= render 'sidebar' %>
# RSpec
RSpec.describe PostsController, type: :controller do
  describe 'GET #show' do
    before { get :show }
    it { should render_template('show') }
    it { should render_template(partial: '_sidebar') }
  end
end
# Minitest (Shoulda)
class PostsControllerTest < ActionController::TestCase
  context 'GET #show' do
    setup { get :show }
    should render_template('show')
    should render_template(partial: '_sidebar')
  end
end
  
      39 40 41  | 
    
      # File 'lib/shoulda/matchers/action_controller/render_template_matcher.rb', line 39 def render_template(options = {}, message = nil) RenderTemplateMatcher.new(options, message, self) end  | 
  
#render_with_layout(expected_layout = nil) ⇒ RenderWithLayoutMatcher
The render_with_layout matcher asserts that an action is rendered with
a particular layout.
class PostsController < ApplicationController
  def show
    render layout: 'posts'
  end
end
# RSpec
RSpec.describe PostsController, type: :controller do
  describe 'GET #show' do
    before { get :show }
    it { should render_with_layout('posts') }
  end
end
# Minitest (Shoulda)
class PostsControllerTest < ActionController::TestCase
  context 'GET #show' do
    setup { get :show }
    should render_with_layout('posts')
  end
end
It can also be used to assert that the action is not rendered with a layout at all:
class PostsController < ApplicationController
  def sidebar
    render layout: false
  end
end
# RSpec
RSpec.describe PostsController, type: :controller do
  describe 'GET #sidebar' do
    before { get :sidebar }
    it { should_not render_with_layout }
  end
end
# Minitest (Shoulda)
class PostsControllerTest < ActionController::TestCase
  context 'GET #sidebar' do
    setup { get :sidebar }
    should_not render_with_layout
  end
end
  
      60 61 62  | 
    
      # File 'lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb', line 60 def render_with_layout(expected_layout = nil) RenderWithLayoutMatcher.new(expected_layout).in_context(self) end  | 
  
#rescue_from(exception) ⇒ RescueFromMatcher
The rescue_from matcher tests usage of the rescue_from macro. It
asserts that an exception and method are present in the list of
exception handlers, and that the handler method exists.
class ApplicationController < ActionController::Base
  rescue_from ActiveRecord::RecordNotFound, with: :handle_not_found
  private
  def handle_not_found
    # ...
  end
end
# RSpec
RSpec.describe ApplicationController, type: :controller do
  it do
    should rescue_from(ActiveRecord::RecordNotFound).
      with(:handle_not_found)
  end
end
# Minitest (Shoulda)
class ApplicationControllerTest < ActionController::TestCase
  should rescue_from(ActiveRecord::RecordNotFound).
    with(:handle_not_found)
end
  
      34 35 36  | 
    
      # File 'lib/shoulda/matchers/action_controller/rescue_from_matcher.rb', line 34 def rescue_from(exception) RescueFromMatcher.new exception end  | 
  
#respond_with(status) ⇒ RespondWithMatcher
The respond_with matcher tests that an action responds with a certain
status code.
You can specify that the status should be a number:
class PostsController < ApplicationController
  def index
    render status: 403
  end
end
# RSpec
RSpec.describe PostsController, type: :controller do
  describe 'GET #index' do
    before { get :index }
    it { should respond_with(403) }
  end
end
# Minitest (Shoulda)
class PostsControllerTest < ActionController::TestCase
  context 'GET #index' do
    setup { get :index }
    should respond_with(403)
  end
end
You can specify that the status should be within a range of numbers:
class PostsController < ApplicationController
  def destroy
    render status: 508
  end
end
# RSpec
RSpec.describe PostsController, type: :controller do
  describe 'DELETE #destroy' do
    before { delete :destroy }
    it { should respond_with(500..600) }
  end
end
# Minitest (Shoulda)
class PostsControllerTest < ActionController::TestCase
  context 'DELETE #destroy' do
    setup { delete :destroy }
    should respond_with(500..600)
  end
end
Finally, you can specify that the status should be a symbol:
class PostsController < ApplicationController
  def show
    render status: :locked
  end
end
# RSpec
RSpec.describe PostsController, type: :controller do
  describe 'GET #show' do
    before { get :show }
    it { should respond_with(:locked) }
  end
end
# Minitest (Shoulda)
class PostsControllerTest < ActionController::TestCase
  context 'GET #show' do
    setup { get :show }
    should respond_with(:locked)
  end
end
  
      87 88 89  | 
    
      # File 'lib/shoulda/matchers/action_controller/respond_with_matcher.rb', line 87 def respond_with(status) RespondWithMatcher.new(status) end  | 
  
#route(method, path, port: nil) ⇒ RouteMatcher
The route matcher tests that a route resolves to a controller,
action, and params; and that the controller, action, and params
generates the same route. For an RSpec suite, this is like using a
combination of route_to and be_routable. In a test suite using
Minitest + Shoulda, it provides a more expressive syntax over
assert_routing.
You can use this matcher either in a controller test case or in a routing test case. For instance, given these routes:
My::Application.routes.draw do
  get '/posts', to: 'posts#index'
  get '/posts/:id', to: 'posts#show'
end
You could choose to write tests for these routes alongside other tests for PostsController:
class PostsController < ApplicationController
  # ...
end
# RSpec
RSpec.describe PostsController, type: :controller do
  it { should route(:get, '/posts').to(action: :index) }
  it { should route(:get, '/posts/1').to(action: :show, id: 1) }
end
# Minitest (Shoulda)
class PostsControllerTest < ActionController::TestCase
  should route(:get, '/posts').to(action: 'index')
  should route(:get, '/posts/1').to(action: :show, id: 1)
end
Or you could place the tests along with other route tests:
# RSpec
describe 'Routing', type: :routing do
  it do
    should route(:get, '/posts').
      to(controller: :posts, action: :index)
  end
  it do
    should route(:get, '/posts/1').
      to('posts#show', id: 1)
  end
end
# Minitest (Shoulda)
class RoutesTest < ActionController::IntegrationTest
  should route(:get, '/posts').
    to(controller: :posts, action: :index)
  should route(:get, '/posts/1').
    to('posts#show', id: 1)
end
Notice that in the former case, as we are inside of a test case for
PostsController, we do not have to specify that the routes resolve to
this controller. In the latter case we specify this using the
controller key passed to the to qualifier.
Specifying a port
If the route you're testing has a constraint on it that limits the route
to a particular port, you can specify it by passing a port option to
the matcher:
class PortConstraint
  def initialize(port)
    @port = port
  end
  def matches?(request)
    request.port == @port
  end
end
My::Application.routes.draw do
  get '/posts',
    to: 'posts#index',
    constraints: PortConstraint.new(12345)
end
# RSpec
describe 'Routing', type: :routing do
  it do
    should route(:get, '/posts', port: 12345).
      to('posts#index')
  end
end
# Minitest (Shoulda)
class RoutesTest < ActionController::IntegrationTest
  should route(:get, '/posts', port: 12345).
    to('posts#index')
end
Qualifiers
to
Use to to specify the action (along with the controller, if needed)
that the route resolves to.
to takes either keyword arguments (controller and action) or a
string that represents the controller/action pair:
route(:get, '/posts').to(action: index)
route(:get, '/posts').to(controller: :posts, action: index)
route(:get, '/posts').to('posts#index')
If there are parameters in your route, then specify those too:
route(:get, '/posts/1').to('posts#show', id: 1)
You may also specify special parameters such as :format:
route(:get, '/posts').to('posts#index', format: :json)
  
      127 128 129  | 
    
      # File 'lib/shoulda/matchers/action_controller/route_matcher.rb', line 127 def route(method, path, port: nil) RouteMatcher.new(self, method, path, port: port) end  | 
  
#set_flash ⇒ SetFlashMatcher
The set_flash matcher is used to make assertions about the
flash hash.
class PostsController < ApplicationController
  def index
    flash[:foo] = 'A candy bar'
  end
  def destroy
  end
end
# RSpec
RSpec.describe PostsController, type: :controller do
  describe 'GET #index' do
    before { get :index }
    it { should set_flash }
  end
  describe 'DELETE #destroy' do
    before { delete :destroy }
    it { should_not set_flash }
  end
end
# Minitest (Shoulda)
class PostsControllerTest < ActionController::TestCase
  context 'GET #index' do
    setup { get :index }
    should set_flash
  end
  context 'DELETE #destroy' do
    setup { delete :destroy }
    should_not set_flash
  end
end
Qualifiers
[]
Use [] to narrow the scope of the matcher to a particular key.
class PostsController < ApplicationController
  def index
    flash[:foo] = 'A candy bar'
  end
end
# RSpec
RSpec.describe PostsController, type: :controller do
  describe 'GET #index' do
    before { get :index }
    it { should set_flash[:foo] }
    it { should_not set_flash[:bar] }
  end
end
# Minitest (Shoulda)
class PostsControllerTest < ActionController::TestCase
  context 'GET #index' do
    setup { get :show }
    should set_flash[:foo]
    should_not set_flash[:bar]
  end
end
to
Use to to assert that some key was set to a particular value, or that
some key matches a particular regex.
class PostsController < ApplicationController
  def index
    flash[:foo] = 'A candy bar'
  end
end
# RSpec
RSpec.describe PostsController, type: :controller do
  describe 'GET #index' do
    before { get :index }
    it { should set_flash.to('A candy bar') }
    it { should set_flash.to(/bar/) }
    it { should set_flash[:foo].to('bar') }
    it { should_not set_flash[:foo].to('something else') }
  end
end
# Minitest (Shoulda)
class PostsControllerTest < ActionController::TestCase
  context 'GET #index' do
    setup { get :show }
    should set_flash.to('A candy bar')
    should set_flash.to(/bar/)
    should set_flash[:foo].to('bar')
    should_not set_flash[:foo].to('something else')
  end
end
now
Use now to change the scope of the matcher to use the "now" hash
instead of the usual "future" hash.
class PostsController < ApplicationController
  def show
    flash.now[:foo] = 'bar'
  end
end
# RSpec
RSpec.describe PostsController, type: :controller do
  describe 'GET #show' do
    before { get :show }
    it { should set_flash.now }
    it { should set_flash.now[:foo] }
    it { should set_flash.now[:foo].to('bar') }
  end
end
# Minitest (Shoulda)
class PostsControllerTest < ActionController::TestCase
  context 'GET #index' do
    setup { get :show }
    should set_flash.now
    should set_flash.now[:foo]
    should set_flash.now[:foo].to('bar')
  end
end
  
      150 151 152  | 
    
      # File 'lib/shoulda/matchers/action_controller/set_flash_matcher.rb', line 150 def set_flash SetFlashMatcher.new.in_context(self) end  | 
  
#set_session ⇒ SetSessionMatcher
The set_session matcher is used to make assertions about the
session hash.
class PostsController < ApplicationController
  def index
    session[:foo] = 'A candy bar'
  end
  def destroy
  end
end
# RSpec
RSpec.describe PostsController, type: :controller do
  describe 'GET #index' do
    before { get :index }
    it { should set_session }
  end
  describe 'DELETE #destroy' do
    before { delete :destroy }
    it { should_not set_session }
  end
end
# Minitest (Shoulda)
class PostsControllerTest < ActionController::TestCase
  context 'GET #index' do
    setup { get :index }
    should set_session
  end
  context 'DELETE #destroy' do
    setup { delete :destroy }
    should_not set_session
  end
end
Qualifiers
[]
Use [] to narrow the scope of the matcher to a particular key.
class PostsController < ApplicationController
  def index
    session[:foo] = 'A candy bar'
  end
end
# RSpec
RSpec.describe PostsController, type: :controller do
  describe 'GET #index' do
    before { get :index }
    it { should set_session[:foo] }
    it { should_not set_session[:bar] }
  end
end
# Minitest (Shoulda)
class PostsControllerTest < ActionController::TestCase
  context 'GET #index' do
    setup { get :show }
    should set_session[:foo]
    should_not set_session[:bar]
  end
end
to
Use to to assert that some key was set to a particular value, or that
some key matches a particular regex.
class PostsController < ApplicationController
  def index
    session[:foo] = 'A candy bar'
  end
end
# RSpec
RSpec.describe PostsController, type: :controller do
  describe 'GET #index' do
    before { get :index }
    it { should set_session.to('A candy bar') }
    it { should set_session.to(/bar/) }
    it { should set_session[:foo].to('bar') }
    it { should_not set_session[:foo].to('something else') }
  end
end
# Minitest (Shoulda)
class PostsControllerTest < ActionController::TestCase
  context 'GET #index' do
    setup { get :show }
    should set_session.to('A candy bar')
    should set_session.to(/bar/)
    should set_session[:foo].to('bar')
    should_not set_session[:foo].to('something else')
  end
end
  
      117 118 119  | 
    
      # File 'lib/shoulda/matchers/action_controller/set_session_matcher.rb', line 117 def set_session SetSessionMatcher.new.in_context(self) end  | 
  
#use_after_action(callback) ⇒ CallbackMatcher
The use_after_action matcher is used to test that an after_action
callback is defined within your controller.
class IssuesController < ApplicationController
  after_action :log_activity
end
# RSpec
RSpec.describe IssuesController, type: :controller do
  it { should use_after_action(:log_activity) }
  it { should_not use_after_action(:destroy_user) }
end
# Minitest (Shoulda)
class IssuesControllerTest < ActionController::TestCase
  should use_after_action(:log_activity)
  should_not use_after_action(:destroy_user)
end
  
      50 51 52  | 
    
      # File 'lib/shoulda/matchers/action_controller/callback_matcher.rb', line 50 def use_after_action(callback) CallbackMatcher.new(callback, :after, :action) end  | 
  
#use_around_action(callback) ⇒ CallbackMatcher
The use_around_action matcher is used to test that an around_action
callback is defined within your controller.
class ChangesController < ApplicationController
  around_action :wrap_in_transaction
end
# RSpec
RSpec.describe ChangesController, type: :controller do
  it { should use_around_action(:wrap_in_transaction) }
  it { should_not use_around_action(:save_view_context) }
end
# Minitest (Shoulda)
class ChangesControllerTest < ActionController::TestCase
  should use_around_action(:wrap_in_transaction)
  should_not use_around_action(:save_view_context)
end
  
      75 76 77  | 
    
      # File 'lib/shoulda/matchers/action_controller/callback_matcher.rb', line 75 def use_around_action(callback) CallbackMatcher.new(callback, :around, :action) end  | 
  
#use_before_action(callback) ⇒ CallbackMatcher
The use_before_action matcher is used to test that a before_action
callback is defined within your controller.
class UsersController < ApplicationController
  before_action :authenticate_user!
end
# RSpec
RSpec.describe UsersController, type: :controller do
  it { should use_before_action(:authenticate_user!) }
  it { should_not use_before_action(:prevent_ssl) }
end
# Minitest (Shoulda)
class UsersControllerTest < ActionController::TestCase
  should use_before_action(:authenticate_user!)
  should_not use_before_action(:prevent_ssl)
end
  
      25 26 27  | 
    
      # File 'lib/shoulda/matchers/action_controller/callback_matcher.rb', line 25 def use_before_action(callback) CallbackMatcher.new(callback, :before, :action) end  |