Don't miss

I created an open source development tool to quickly & dynamically mock API endpoints. Check it out at GitHub: DuckRails

Heads up

I will no longer maintain arubystory. For new posts, find me at my personal blog. CU there!

Tuesday, October 28, 2014

Testing execution of a before_filter with RSpec custom matcher

UPDATE: I created my first gem to cover the functionality of this post. Visit its homepage here.

I was working on my application and I wanted to write some RSpec tests for some controllers having before_filter for some actions. I didn't want to explicitly test the behaviour of the filter since it was already tested in another context. I only wanted to test that it is being executed.



Let's assume that this is our controller:
class FooController < ApplicationController
  before_filter :first_filter
  before_filter :second_filter, :only => [:edit, :update]

  def index
    # some code
  end

  def edit
    #some code
  end

  def update
    # some code
  end

  protected

  def first_filter
    # do something
  end

  def second_filter
    # do something
  end
end


So, we can write the following custom matcher (I placed it under spec/support/matchers/filters.rb):

RSpec::Matchers.define :execute_before_filter do |filter_name, options|
  match do |controller|
    controller.stubs(filter_name).raises(StandardError.new("Filter executed: #{filter_name}"))

    if options[:stub_filters]
      options[:stub_filters].each do |filter|
        controller.stubs(filter).returns(true)
      end
    end

    result = begin
      send(options[:via], options[:on], options[:with])
      false
    rescue StandardError => e
      e.message == "Filter executed: #{filter_name}"
    rescue
      false
    end
    result
  end

  failure_message do |actual|
    filter = expected[0]
    options = expected[1]
    action = options[:on]
    with = options[:via]
    params = options[:with]
    message = "expected #{actual.class} to execute filter #{filter}"
    message << " before action #{action}"
    message << " [requested via #{with} with params '#{params}']."
  end

  failure_message_when_negated do |actual|
    filter = expected[0]
    options = expected[1]
    action = options[:on]
    with = options[:via]
    params = options[:with]
    message = "expected #{actual.class} not to execute filter #{filter}"
    message << " before action #{action}"
    message << " [requested via #{with} with params '#{params}']"
  end
end

and then in our controller's spec we test the filter execution with the following code:

require 'rails_helper'

RSpec.describe FooController, :type => :controller do

  it { should execute_before_filter :first_filter, :on => :index, :via => :get }

  it { should execute_before_filter :first_filter, :on => :edit, :via => :get, :with => { :id => '1' } }
  it { should execute_before_filter :second_filter,
                                    :on => :edit,
                                    :via => :get,
                                    :with => { :id => '1' },
                                    :stub_filters => [:first_filter] }

  it { should execute_before_filter :first_filter, :on => :update, :via => :put, :with => { :id => '1' } }
  it { should execute_before_filter :second_filter,
                                    :on => :update,
                                    :via => :put,
                                    :with => { :id => :'1' },
                                    :stub_filters => [:first_filter] }
end

We use the :ignore_filters to stub possible prepended filters so that in case they brake due to missing stubbings etc, they don't mess with our test.

If we comment the before_filter declarations int the FooController =>
Tadaaaaa:


Post a Comment