module Shoulda::Matchers::ActionController

Public Instance Methods

filter_param(key) click to toggle source

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

@return [FilterParamMatcher]

# File lib/shoulda/matchers/action_controller/filter_param_matcher.rb, line 24
def filter_param(key)
  FilterParamMatcher.new(key)
end
permit(*params) click to toggle source

The `permit` matcher tests that an action in your controller receives a whitelist 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

@return [PermitMatcher]

# 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) click to toggle source

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_path }
    should redirect_to(action: :index)
  end
end

@return [RedirectToMatcher]

# 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) click to toggle source

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

@return [RenderTemplateMatcher]

# 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) click to toggle source

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

@return [RenderWithLayoutMatcher]

# 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) click to toggle source

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

@return [RescueFromMatcher]

# File lib/shoulda/matchers/action_controller/rescue_from_matcher.rb, line 34
def rescue_from(exception)
  RescueFromMatcher.new exception
end
respond_with(status) click to toggle source

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

@return [RespondWithMatcher]

# File lib/shoulda/matchers/action_controller/respond_with_matcher.rb, line 87
def respond_with(status)
  RespondWithMatcher.new(status)
end
route(method, path) click to toggle source

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', controller: 'posts', action: 'index'
  get '/posts/:id' => '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.

#### Qualifiers

##### to

Use `to` to specify the action (along with the controller, if needed) that the route resolves to.

# Three ways of saying the same thing (using the example above)
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)

@return [RouteMatcher]

# File lib/shoulda/matchers/action_controller/route_matcher.rb, line 89
def route(method, path)
  RouteMatcher.new(method, path, self)
end
set_flash() click to toggle source

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

@return [SetFlashMatcher]

# File lib/shoulda/matchers/action_controller/set_flash_matcher.rb, line 150
def set_flash
  SetFlashMatcher.new.in_context(self)
end
set_session() click to toggle source

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

@return [SetSessionMatcher]

# 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) click to toggle source

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

@return [CallbackMatcher]

# File lib/shoulda/matchers/action_controller/callback_matcher.rb, line 108
def use_after_action(callback)
  CallbackMatcher.new(callback, :after, :action)
end
use_after_filter(callback) click to toggle source
# File lib/shoulda/matchers/action_controller/callback_matcher.rb, line 57
def use_after_filter(callback)
  CallbackMatcher.new(callback, :after, :filter)
end
use_around_action(callback) click to toggle source

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

@return [CallbackMatcher]

# File lib/shoulda/matchers/action_controller/callback_matcher.rb, line 162
def use_around_action(callback)
  CallbackMatcher.new(callback, :around, :action)
end
use_around_filter(callback) click to toggle source
# File lib/shoulda/matchers/action_controller/callback_matcher.rb, line 136
def use_around_filter(callback)
  CallbackMatcher.new(callback, :around, :filter)
end
use_before_action(callback) click to toggle source

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

@return [CallbackMatcher]

# File lib/shoulda/matchers/action_controller/callback_matcher.rb, line 83
def use_before_action(callback)
  CallbackMatcher.new(callback, :before, :action)
end
use_before_filter(callback) click to toggle source
# File lib/shoulda/matchers/action_controller/callback_matcher.rb, line 28
def use_before_filter(callback)
  CallbackMatcher.new(callback, :before, :filter)
end