Skip to content

Commit

Permalink
Add CallbackMatcher for ActionController
Browse files Browse the repository at this point in the history
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
Damian Galarza and Harry Schwartz authored and Damian Galarza committed Mar 7, 2014
1 parent fb77a6c commit b508346
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 1 deletion.
42 changes: 41 additions & 1 deletion README.md
Expand Up @@ -909,7 +909,7 @@ end


### ActionController Matchers ### 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 #### filter_param


Expand Down Expand Up @@ -1251,6 +1251,46 @@ class PostsControllerTest < ActionController::TestCase
end 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 ## Versioning


shoulda-matchers follows Semantic Versioning 2.0 as defined at shoulda-matchers follows Semantic Versioning 2.0 as defined at
Expand Down
1 change: 1 addition & 0 deletions lib/shoulda/matchers/action_controller.rb
Expand Up @@ -8,6 +8,7 @@
require 'shoulda/matchers/action_controller/redirect_to_matcher' require 'shoulda/matchers/action_controller/redirect_to_matcher'
require 'shoulda/matchers/action_controller/render_template_matcher' require 'shoulda/matchers/action_controller/render_template_matcher'
require 'shoulda/matchers/action_controller/rescue_from_matcher' require 'shoulda/matchers/action_controller/rescue_from_matcher'
require 'shoulda/matchers/action_controller/callback_matcher'


module Shoulda module Shoulda
module Matchers module Matchers
Expand Down
100 changes: 100 additions & 0 deletions lib/shoulda/matchers/action_controller/callback_matcher.rb
@@ -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
79 changes: 79 additions & 0 deletions spec/shoulda/matchers/action_controller/callback_matcher_spec.rb
@@ -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.