Permalink
Browse files

Add CallbackMatcher for ActionController

Matcher for checking if a filter / action callback is in place within a
controller.

Example:

```ruby
describe UserController do
  it { should use_before_filter(:authenticate_user!) }
end
```
  • Loading branch information...
1 parent fb77a6c commit b5083467bcfe2ef039ffb85d9c1042329d2eb20d Damian Galarza and Harry Schwartz committed with dgalarza Jan 31, 2014
View
@@ -909,7 +909,7 @@ end
### ActionController Matchers
-*Jump to: [filter_param](#filter_param), [redirect_to](#redirect_to), [render_template](#render_template), [render_with_layout](#render_with_layout), [rescue_from](#rescue_from), [respond_with](#respond_with), [route](#route), [set_session](#set_session), [set_the_flash](#set_the_flash)*
+*Jump to: [filter_param](#filter_param), [redirect_to](#redirect_to), [render_template](#render_template), [render_with_layout](#render_with_layout), [rescue_from](#rescue_from), [respond_with](#respond_with), [route](#route), [set_session](#set_session), [set_the_flash](#set_the_flash), [use_after_filter](#use_after_filter), [use_before_filter](#use_before_filter)*
#### filter_param
@@ -1251,6 +1251,46 @@ class PostsControllerTest < ActionController::TestCase
end
```
+#### use_after_filter
+
+The `use_after_filter` ensures a given `after_filter` is used.
+
+```ruby
+class UserController < ActionController::Base
+ after_filter :log_activity
+end
+
+# RSpec
+describe UserController do
+ it { should use_after_filter(:log_activity) }
+end
+
+# Test::Unit
+class UserControllerTest < ActionController::TestCase
+ should use_after_filter(:log_activity)
+end
+```
+
+#### use_before_filter
+
+The `use_before_filter` ensures a given `before_filter` is used.
+
+```ruby
+class UserController < ActionController::Base
+ before_filter :authenticate_user!
+end
+
+# RSpec
+describe UserController do
+ it { should use_before_filter(:authenticate_user!) }
+end
+
+# Test::Unit
+class UserControllerTest < ActionController::TestCase
+ should use_before_filter(:authenticate_user!)
+end
+```
+
## Versioning
shoulda-matchers follows Semantic Versioning 2.0 as defined at
@@ -8,6 +8,7 @@
require 'shoulda/matchers/action_controller/redirect_to_matcher'
require 'shoulda/matchers/action_controller/render_template_matcher'
require 'shoulda/matchers/action_controller/rescue_from_matcher'
+require 'shoulda/matchers/action_controller/callback_matcher'
module Shoulda
module Matchers
@@ -0,0 +1,100 @@
+module Shoulda # :nodoc:
+ module Matchers
+ module ActionController # :nodoc:
+ # Ensure a controller uses a given before_filter
+ #
+ # Example:
+ #
+ # it { should use_before_filter(:authenticate_user!) }
+ # it { should_not use_before_filter(:prevent_ssl) }
+ def use_before_filter(callback)
+ CallbackMatcher.new(callback, :before, :filter)
+ end
+
+ # Ensure a controller uses a given before_filter
+ #
+ # Example:
+ #
+ # it { should use_after_filter(:log_activity) }
+ # it { should_not use_after_filter(:destroy_user) }
+ def use_after_filter(callback)
+ CallbackMatcher.new(callback, :after, :filter)
+ end
+
+ # Ensure a controller uses a given before_action
+ #
+ # Example:
+ #
+ # it { should use_before_action(:authenticate_user!) }
+ # it { should_not use_before_action(:prevent_ssl) }
+ def use_before_action(callback)
+ CallbackMatcher.new(callback, :before, :action)
+ end
+
+ # Ensure a controller uses a given after_action
+ #
+ # Example:
+ #
+ # it { should use_after_action(:log_activity) }
+ # it { should_not use_after_action(:destroy_user) }
+ def use_after_action(callback)
+ CallbackMatcher.new(callback, :after, :action)
+ end
+
+ # Ensure a controller uses a given around_filter
+ #
+ # Example:
+ #
+ # it { should use_around_filter(:log_activity) }
+ # it { should_not use_around_filter(:destroy_user) }
+ def use_around_filter(callback)
+ CallbackMatcher.new(callback, :around, :filter)
+ end
+
+ # Ensure a controller uses a given around_action
+ #
+ # Example:
+ #
+ # it { should use_around_action(:log_activity) }
+ # it { should_not use_around_action(:destroy_user) }
+ def use_around_action(callback)
+ CallbackMatcher.new(callback, :around, :action)
+ end
+
+ class CallbackMatcher # :nodoc:
+ def initialize(method_name, kind, callback_type)
+ @method_name = method_name
+ @kind = kind
+ @callback_type = callback_type
+ end
+
+ def matches?(subject)
+ @subject = subject
+ callbacks.map(&:filter).include?(method_name)
+ end
+
+ def failure_message
+ "Expected that #{subject.name} would have :#{method_name} as a #{kind}_#{callback_type}"
+ end
+ alias failure_message_for_should failure_message
+
+ def failure_message_when_negated
+ "Expected that #{subject.name} would not have :#{method_name} as a #{kind}_#{callback_type}"
+ end
+ alias failure_message_for_should_not failure_message_when_negated
+
+ def description
+ "have :#{method_name} as a #{kind}_#{callback_type}"
+ end
+
+ private
+
+ def callbacks
+ subject._process_action_callbacks.select { |callback| callback.kind == kind }
+ end
+
+ attr_reader :method_name, :subject, :kind, :callback_type
+ end
+ end
+ end
+end
@@ -0,0 +1,79 @@
+require 'spec_helper'
+
+describe Shoulda::Matchers::ActionController::CallbackMatcher do
+ shared_examples 'CallbackMatcher' do |kind, callback_type|
+ let(:matcher) { described_class.new(:authenticate_user!, kind, callback_type) }
+ let(:controller) { define_controller('HookController') }
+
+ describe '#matches?' do
+ it "matches when a #{kind} hook is in place" do
+ add_callback(kind, callback_type, :authenticate_user!)
+
+ expect(matcher.matches?(controller)).to be_true
+ end
+
+ it "does not match when a #{kind} hook is missing" do
+ expect(matcher.matches?(controller)).to be_false
+ end
+ end
+
+ describe 'description' do
+ it 'includes the filter kind and name' do
+ expect(matcher.description).to eq "have :authenticate_user! as a #{kind}_#{callback_type}"
+ end
+ end
+
+ describe 'failure message' do
+ it 'includes the filter kind and name that was expected' do
+ message = "Expected that HookController would have :authenticate_user! as a #{kind}_#{callback_type}"
+
+ expect {
+ expect(controller).to send("use_#{kind}_#{callback_type}", :authenticate_user!)
+ }.to fail_with_message(message)
+ end
+ end
+
+ describe 'failure message when negated' do
+ it 'includes the filter kind and name that was expected' do
+ add_callback(kind, callback_type, :authenticate_user!)
+ message = "Expected that HookController would not have :authenticate_user! as a #{kind}_#{callback_type}"
+
+ expect {
+ expect(controller).not_to send("use_#{kind}_#{callback_type}", :authenticate_user!)
+ }.to fail_with_message(message)
+ end
+ end
+
+ private
+
+ def add_callback(kind, callback_type, callback)
+ controller.send("#{kind}_#{callback_type}", callback)
+ end
+ end
+
+ describe '#use_before_filter' do
+ it_behaves_like 'CallbackMatcher', :before, :filter
+ end
+
+ describe '#use_after_filter' do
+ it_behaves_like 'CallbackMatcher', :after, :filter
+ end
+
+ describe '#use_around_filter' do
+ it_behaves_like 'CallbackMatcher', :around, :filter
+ end
+
+ if rails_4_x?
+ describe '#use_before_action' do
+ it_behaves_like 'CallbackMatcher', :before, :action
+ end
+
+ describe '#use_after_action' do
+ it_behaves_like 'CallbackMatcher', :after, :action
+ end
+
+ describe '#use_around_action' do
+ it_behaves_like 'CallbackMatcher', :around, :action
+ end
+ end
+end

0 comments on commit b508346

Please sign in to comment.