module Shoulda::Matchers::ActionController
This module provides matchers that are used to test behavior within controllers.
Public Instance Methods
Source
# File lib/shoulda/matchers/action_controller/filter_param_matcher.rb, line 24 def filter_param(key) FilterParamMatcher.new(key) end
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]
Source
# File lib/shoulda/matchers/action_controller/permit_matcher.rb, line 203 def permit(*params) PermitMatcher.new(params).in_context(self) end
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]
Source
# 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
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
@return [RedirectToMatcher]
Source
# File lib/shoulda/matchers/action_controller/render_template_matcher.rb, line 39 def render_template(options = {}, message = nil) RenderTemplateMatcher.new(options, message, self) end
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]
Source
# 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
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]
Source
# File lib/shoulda/matchers/action_controller/rescue_from_matcher.rb, line 34 def rescue_from(exception) RescueFromMatcher.new exception end
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]
Source
# File lib/shoulda/matchers/action_controller/respond_with_matcher.rb, line 87 def respond_with(status) RespondWithMatcher.new(status) end
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]
Source
# 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
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)
@return [RouteMatcher]
Source
# File lib/shoulda/matchers/action_controller/set_flash_matcher.rb, line 150 def set_flash SetFlashMatcher.new.in_context(self) end
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]
Source
# File lib/shoulda/matchers/action_controller/set_session_matcher.rb, line 117 def set_session SetSessionMatcher.new.in_context(self) end
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]
Source
# File lib/shoulda/matchers/action_controller/callback_matcher.rb, line 50 def use_after_action(callback) CallbackMatcher.new(callback, :after, :action) end
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]
Source
# File lib/shoulda/matchers/action_controller/callback_matcher.rb, line 75 def use_around_action(callback) CallbackMatcher.new(callback, :around, :action) end
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]
Source
# File lib/shoulda/matchers/action_controller/callback_matcher.rb, line 25 def use_before_action(callback) CallbackMatcher.new(callback, :before, :action) end
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]